Type Narrowing Best Practices
In this page:
Narrow Early
Check invalid or unsupported cases as early as possible inside a function — narrowing early reduces nesting and keeps the rest of the function's logic operating on an already-validated, simpler type.
Example: Narrow Early
function process(value: string | null) {
if (value === null) return;
console.log(value.toUpperCase());
}
process("hello");
Use Built-in Guards
Prefer standard runtime checks like typeof, instanceof, Array.isArray, and the in operator over custom logic wherever they apply — TypeScript understands all of these natively and they communicate intent clearly to other readers.
Example: Use Built-in Guards
function describe(value: string | number | string[]) {
if (Array.isArray(value)) return "array";
if (typeof value === "string") return "string";
return "number";
}
console.log(describe([1, 2]));
Use Discriminants
For a family of related object variants, add a shared literal discriminant property such as kind or status — checking that one property gives TypeScript a completely reliable way to select the correct variant in a union.
Example: Use Discriminants
type Shape = { kind: "circle"; radius: number } | { kind: "square"; side: number };
function area(shape: Shape) {
return shape.kind === "circle" ? Math.PI * shape.radius ** 2 : shape.side ** 2;
}
console.log(area({ kind: "square", side: 4 }));
Write Custom Type Guards
When built-in guards genuinely aren't enough, write a custom function that returns a type predicate like value is User — just keep these checks scrupulously accurate, since TypeScript trusts whatever the predicate claims without verifying it further.
Example: Write Custom Type Guards
interface User { name: string; }
function isUser(value: unknown): value is User {
return typeof (value as User)?.name === "string";
}
const data: unknown = { name: "Ravi" };
if (isUser(data)) console.log(data.name);
Avoid Unnecessary Assertions
Avoid reaching for a type assertion just to silence an error — an assertion tells the compiler to trust you without any runtime check, so prefer a real guard whenever the value originates from users, APIs, storage, or any other untrusted source.
Example: Avoid Unnecessary Assertions
function getLength(value: unknown): number {
if (typeof value === "string") return value.length;
return 0;
}
console.log(getLength("hello"));
Chapter Quiz — Complete all 7 topics to unlock
0/7 topics done
Complete these topics first: