Discriminated unions, also known as tagged unions or algebraic data types, are a powerful feature in TypeScript that allows you to create types which are a union of other types, but can be easily differentiated based on a common, literal property.
A discriminated union typically consists of:
- A common property (the "discriminant") with literal types
- A union of types that each have this property
type Square = {
kind: "square";
size: number;
};
type Circle = {
kind: "circle";
radius: number;
};
type Shape = Square | Circle;
Here, kind
is the discriminant property.
The discriminant allows TypeScript to narrow down the type within conditional blocks:
function area(shape: Shape): number {
switch (shape.kind) {
case "square":
return shape.size * shape.size;
case "circle":
return Math.PI * shape.radius ** 2;
}
}
TypeScript can perform exhaustiveness checking with discriminated unions:
function assertNever(x: never): never {
throw new Error("Unexpected object: " + x);
}
function area(shape: Shape): number {
switch (shape.kind) {
case "square":
return shape.size * shape.size;
case "circle":
return Math.PI * shape.radius ** 2;
default:
return assertNever(shape);
}
}
If we add a new shape without updating this function, TypeScript will give us an error.
Discriminated unions are particularly useful for:
- Modelling state in applications
- Handling different types of API responses
- Representing different variants of a data structure
They provide a way to model complex data structures whilst maintaining type safety and enabling powerful type inference.