Oct 18 2024
Imagine we have a Vehicle type in our app that we can represent like this:
type Vehicle = Car | Motorcycle | Bike;
type Car = {
brand: string;
model: string;
engine: string;
nrOfDoors: number;
hasClimateControl: boolean;
};
type Motorcycle = {
brand: string;
model: string;
engine: string;
};
type Bike = {
brand: string;
model: string;
type: string;
nrOfWheels: number;
};
Now we want to create a vehicle and log nrOfDoors if it is a car. How would we do that?
const vehicle = createRandomVehicle();
// ❌ This won't work
console.log(vehicle.nrOfDoors);
if ("brand" in vehicle) {
// ❌ This won't work
console.log(vehicle.nrOfDoors);
}
// This will work
if ("nrOfDoors" in vehicle) {
console.log(vehicle.nrOfDoors);
}
// This will work too
if ("hasClimateControl" in vehicle) {
console.log(vehicle.nrOfDoors);
}
First, we need to check if the vehicle has a nrOfDoors or hasClimateControl property. Then TypeScript will know that the vehicle is a Car, and we can access the nrOfDoors property.
The process when TypeScript “gets to know” that the vehicle is a Car is called a discriminated union. It is a pattern that allows TypeScript to narrow down the type of an object based on a specific property.
In simple terms, no other type has the nrOfDoors property, so if we check for it, TypeScript will know that the object is a Car.
But we won’t always know which property to check. In that case, we can use a common property that all types have. In this case, we will use the type property.
type Vehicle = Car | Motorcycle | Bike;
type Car = {
type: "car";
brand: string;
model: string;
engine: string;
nrOfDoors: number;
hasClimateControl: boolean;
};
type Motorcycle = {
type: "motorcycle";
brand: string;
model: string;
engine: string;
};
type Bike = {
type: "bike";
brand: string;
model: string;
type: string;
nrOfWheels: number;
};
We need to make sure that the type property is unique for each type so TypeScript can distinguish between them.
Now we can check for the type property to know which type the vehicle is.
if (vehicle.type === "car") {
console.log(vehicle.nrOfDoors);
}
Also, it helps in various other cases like creating a new Vehicle object manually.
const newVehicle: Vehicle = {
type: "car",
// From this point TypeScript knows that the object is a Car and will suggest only Car properties
};
That’s it. I hope you enjoyed this article.
Here’s an add-on for you: A brief summary of the article. You can use it to create flashcards (e.g., in Anki).
It is a pattern that allows TypeScript to narrow down the type of an object based on a specific property.
Example usage:
type Vehicle = Car | Motorcycle | Bike;
type Car = {
type: "car";
brand: string;
model: string;
engine: string;
nrOfDoors: number;
hasClimateControl: boolean;
};
type Motorcycle = {
type: "motorcycle";
brand: string;
model: string;
engine: string;
};
type Bike = {
type: "bike";
brand: string;
model: string;
type: string;
nrOfWheels: number;
};
if (vehicle.type === "car") {
console.log(vehicle.nrOfDoors);
}