Built-in Type Guards
Type guards narrow a union type to a specific type within a code block. TypeScript understands typeof, instanceof, and in operators.
Example
// typeof guard
function process(value: string | number) {
if (typeof value === "string") {
return value.toUpperCase(); // string methods available
}
return value * 2; // number methods available
}
// instanceof guard
class Dog { bark() { return "Woof!"; } }
class Cat { meow() { return "Meow!"; } }
function speak(animal: Dog | Cat) {
if (animal instanceof Dog) {
return animal.bark();
}
return animal.meow();
} Custom Type Guards
You can create custom type guard functions using the 'is' keyword in the return type.
Example
interface Fish { swim(): void }
interface Bird { fly(): void }
// Custom type guard
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
function move(animal: Fish | Bird) {
if (isFish(animal)) {
animal.swim(); // TypeScript knows it's Fish
} else {
animal.fly(); // TypeScript knows it's Bird
}
}
// Truthiness narrowing
function printName(name: string | null) {
if (name) {
console.log(name.toUpperCase()); // string
}
} Exhaustive Checks with never
Use the never type to ensure all cases in a union are handled. If a new variant is added, TypeScript will flag unhandled cases.
Example
type Shape = "circle" | "square" | "triangle";
function getShape(shape: Shape): string {
switch (shape) {
case "circle":
return "Round";
case "square":
return "Boxy";
case "triangle":
return "Pointy";
default:
// If we miss a case, this line would error
const _exhaustive: never = shape;
return _exhaustive;
}
} 