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:
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:
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.
| Guard | Use it to narrow |
|---|---|
typeof x === 'string' | Primitives: string, number, boolean |
'prop' in obj | Which object shape you have |
x instanceof Class | Which class an object came from |
if (x) truthiness | Rule 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?
✍️ Practice
- Write a function taking
number | booleanthat returns a message, usingtypeofto handle each case. - Define
Square { side: number }andCircle { radius: number }and useinto compute each area.
🏠 Homework
- Write a function
describe(value: string | number | boolean)that uses typeof to return a sentence for each type, with sample outputs.