TypeScript Type Guards
Type guards are a powerful TypeScript feature that helps you gain more control over types within your conditional logic. They allow the compiler to understand which type is currently in use within a specific block of code, enabling better type checking and autocompletion.
Let’s say you have a variable that can be either a string or a number:
let value: string | number = "hello";
If you try to access string-specific properties without checking the type, TypeScript will complain:
console.log(value.toUpperCase()); // Error: Property 'toUpperCase' does not exist on type 'string | number'.
This is where type guards come in. They help you narrow down the type within an if
statement or other conditional logic.
1. typeof
operator: The simplest type guard is the typeof
operator.
if (typeof value === "string") {
console.log(value.toUpperCase()); // Works!
} else {
console.log(value.toFixed(2)); // Works assuming value is a number in this branch
}
Within the if
block, TypeScript now knows that value
is a string, and in the else
block, it infers value
as a number.
2. instanceof
operator: Use instanceof
to check if a value is an instance of a particular class.
class Dog {
bark() {
console.log("Woof!");
}
}
class Cat {
meow() {
console.log("Meow!");
}
}
let pet: Dog | Cat = new Dog();
if (pet instanceof Dog) {
pet.bark(); // Works!
} else {
pet.meow(); // Works!
}
3. User-defined type guards: For more complex scenarios, you can create custom type guard functions. These functions must return a type predicate, which is a special return type that tells TypeScript what type a variable is if the function returns true
.
interface Car {
wheels: number;
model: string;
}
interface Bicycle {
wheels: number;
type: string;
}
function isCar(vehicle: Car | Bicycle): vehicle is Car {
return (vehicle as Car).model !== undefined;
}
let vehicle: Car | Bicycle = { wheels: 4, model: "Sedan" };
if (isCar(vehicle)) {
console.log(vehicle.model); // Works!
} else {
console.log(vehicle.type); // Works!
}
The vehicle is Car
syntax is the type predicate. It asserts that if isCar
returns true
, the vehicle
variable is of type Car
.
Using type guards allows you to write more robust and type-safe code, making your TypeScript projects easier to maintain and debug. They empower you to leverage TypeScript’s full type checking capabilities even when dealing with complex conditional logic.