TypeScript Type Guards
Type guards are expressions that let TypeScript narrow a broad type down to a more specific one at particular points in your code, based on runtime checks like typeof, instanceof, in, and custom predicate functions.
typeof Guards for Primitives
Inside an if statement, checking typeof value === "string" (or "number", "boolean", etc.) narrows a union of primitives to just the matching branch for the rest of that block.
Narrowing a primitive union
function formatValue(value: string | number): string {
if (typeof value === "string") {
return value.trim();
}
return value.toFixed(2);
}
console.log(formatValue(" hi ")); // "hi"
console.log(formatValue(3.14159)); // "3.14"instanceof Guards for Class Instances
instanceof checks whether a value was constructed by a particular class, narrowing a union of class instances to the matching subclass inside the guarded branch.
Narrowing a class union
class ApiError extends Error {
constructor(public statusCode: number, message: string) {
super(message);
}
}
function handle(error: Error | ApiError): void {
if (error instanceof ApiError) {
console.log(`API error ${error.statusCode}: ${error.message}`);
} else {
console.log(`Generic error: ${error.message}`);
}
}
handle(new ApiError(404, "Not found"));
handle(new Error("Something broke"));in Guards and Custom Type Predicates
The in operator checks whether a property exists on an object, which is useful for distinguishing plain object shapes that don't share a class hierarchy. For reusable or more complex checks, write a custom function whose return type is 'param is Type' — a type predicate.
Custom guard built on in
interface Fish { swim(): void; }
interface Bird { fly(): void; }
function isFish(pet: Fish | Bird): pet is Fish {
return "swim" in pet;
}
function move(pet: Fish | Bird): void {
if (isFish(pet)) {
pet.swim(); // narrowed to Fish
} else {
pet.fly(); // narrowed to Bird
}
}- typeof — narrows unions of primitive types like string, number, boolean
- instanceof — narrows unions of class instances by constructor
- in — narrows plain object unions by checking property presence
- custom 'x is Type' predicates — reusable, composable guards for complex logic
- discriminated unions with a literal 'kind' field — an alternative that plays well with switch statements
Exercise: TypeScript Type Guards
Which operator narrows a union based on primitive types like 'string' or 'number'?