Power FeaturesExtra· 35 min read

Narrowing: typeof and in Type Guards

When a value could be several types, type guards let TypeScript figure out which one it is right now.

What you will learn

  • Narrow a union with typeof
  • Narrow object shapes with the in operator
  • Understand why narrowing keeps code safe

The challenge of union types

If a value is string | number, you cannot just call string methods on it — it might be a number. Narrowing means proving to TypeScript which type it is right now, so the right operations become allowed.

Narrow with typeof

The typeof check is the simplest type guard. Inside each branch, TypeScript narrows the value to the matching type:

typeof narrows the union in each branch
function format(value: string | number): string {
  if (typeof value === 'string') {
    return value.toUpperCase();    // here, value is a string
  } else {
    return value.toFixed(2);       // here, value is a number
  }
}

console.log(format('hello'));
console.log(format(3.14159));

Note: Output: HELLO 3.14 Inside the if, TypeScript knows value is a string, so .toUpperCase() is allowed. In the else it is a number, so .toFixed(2) works.

Narrow object shapes with in

For objects, typeof is not enough — every object is just "object". The in operator checks whether a property exists, which tells TypeScript which shape you have:

The in operator narrows between two object shapes
interface Cat { meow: () => string; }
interface Dog { bark: () => string; }

function speak(pet: Cat | Dog): string {
  if ('meow' in pet) {
    return pet.meow();     // narrowed to Cat
  }
  return pet.bark();       // narrowed to Dog
}

console.log(speak({ meow: () => 'Meow!' }));
console.log(speak({ bark: () => 'Woof!' }));

Note: Output: Meow! Woof! Checking 'meow' in pet proves it is a Cat in that branch, so pet.meow() is safe. Otherwise TypeScript knows it must be a Dog.

Why this matters

Watch out: Without narrowing, calling value.toUpperCase() on a string | number is an error — TypeScript refuses because it might be a number. A type guard is how you safely "unlock" the right methods.

GuardUse it to narrow
typeof x === 'string'Primitives: string, number, boolean
'prop' in objWhich object shape you have
x instanceof ClassWhich class an object came from
if (x) truthinessRule out null / undefined

Tip: Narrowing is everyday TypeScript. Whenever you have a union, add a guard (typeof, in, instanceof, or a simple if (x)) and the right operations light up safely inside that branch.

Q. You have a value typed string | number and want to call a string method. What must you do first?

Answer: A type guard such as typeof value === "string" narrows the union inside that branch, so TypeScript safely allows string methods there.

✍️ Practice

  1. Write a function taking number | boolean that returns a message, using typeof to handle each case.
  2. Define Square { side: number } and Circle { radius: number } and use in to compute each area.

🏠 Homework

  1. Write a function describe(value: string | number | boolean) that uses typeof to return a sentence for each type, with sample outputs.
Want to learn this with a mentor?

CodingClave runs guided, project-based training (28-day, 45-day & 6-month batches).

Explore Training →