Type Narrowing
When a value's type is a union — like string | number — TypeScript won't let you use operations specific to just one of those types until you've proven which one it actually is. Narrowing is how you prove it, and TypeScript's checker actually tracks the proof.
function describe(value: string | number): string {
if (typeof value === "string") {
return `String of length ${value.length}`;
}
return `Number: ${value.toFixed(2)}`;
}
console.log(describe("hello"));
console.log(describe(3.14159));String of length 5 Number: 3.14
Inside the if (typeof value === "string") block, TypeScript treats value as a string — value.length is valid there but would be a compile error outside that block, where value could still be a number.
in — checking whether a property exists
The in operator checks whether an object has a given property, and TypeScript uses that check to narrow between two different object shapes.
interface Cat {
meow(): string;
}
interface Dog {
bark(): string;
}
function speak(animal: Cat | Dog): string {
if ("meow" in animal) {
return animal.meow();
}
return animal.bark();
}
const cat: Cat = { meow: () => "Meow!" };
const dog: Dog = { bark: () => "Woof!" };
console.log(speak(cat));
console.log(speak(dog));Meow! Woof!
Next: modules and the standard library — organizing code across files, and a few utility types that build on everything covered so far.
Practice exercise
Write a function count(value: string | number[]): string that returns `string: ${value.length}` if value is a string (checked with typeof), or `array: ${value.length}` otherwise. Print count("hello") and count([1, 2, 3]).