TypeScript Tutorial · TypeScript Basics · Lesson 9 of 25
TypeScript Narrowing
Refine union types with typeof, instanceof, in, equality and type predicates.
What is narrowing?
Inside an if, TypeScript narrows a union to the types that are possible in that branch:
function lengthOf(value: string | string[] | null): number {
if (value === null) return 0;
if (typeof value === "string") return value.length; // value: string
return value.length; // value: string[]
}
console.log(lengthOf("hello"), lengthOf(["a", "b"]), lengthOf(null));
Output
5 2 0
Narrowing techniques
| Check | Narrows by |
|---|---|
typeof x === "string" |
primitive type |
x instanceof Date |
class |
"swim" in animal |
property existence |
x === null, x !== undefined |
equality |
Array.isArray(x) |
arrays |
type Fish = { swim: () => string };
type Bird = { fly: () => string };
function move(animal: Fish | Bird): string {
if ("swim" in animal) return animal.swim();
return animal.fly();
}
function when(value: Date | string): string {
return value instanceof Date ? value.toISOString().slice(0, 10) : value;
}
console.log(move({ swim: () => "swimming" }), move({ fly: () => "flying" }));
console.log(when(new Date(Date.UTC(2026, 0, 2))), when("tomorrow"));
Output
swimming flying 2026-01-02 tomorrow
Type predicates
A function returning value is Type teaches TypeScript a custom check:
interface User { id: number; name: string }
function isUser(value: unknown): value is User {
return typeof value === "object" && value !== null && "id" in value && "name" in value;
}
const data: unknown = JSON.parse('{"id": 1, "name": "Ada"}');
if (isUser(data)) {
console.log(`Valid user: ${data.name}`);
}
console.log(isUser({ id: 2 }));
Output
Valid user: Ada false
Test yourself
What return type makes a function a type predicate?
booleanvalue is UserUser | falseasserts User
Show answer
Answer: value is User