Discriminated Unions
Use a shared literal field to safely tell union members apart.
Syntax
type Shape = Circle | Square;A discriminated union is a union of object types that each share a common literal property — the discriminant. Checking that property tells TypeScript exactly which member you have.
This pattern is the cleanest way to model "one of several kinds of thing", such as different shapes or different event types.
Example
Loading editor…
Press Run to execute the code.
When to use it
- An event system uses a kind literal field to distinguish between different event shapes in a switch statement.
- A result type uses a success boolean to safely discriminate Ok and Err variants without runtime casting.
- A shape calculator uses a type field to determine whether to compute circle area or rectangle area.
More examples
Shape discriminated union
Uses a shared kind literal property to safely narrow between Circle and Rect inside a switch statement.
type Circle = { kind: 'circle'; radius: number };
type Rect = { kind: 'rect'; width: number; height: number };
type Shape = Circle | Rect;
function area(shape: Shape): number {
switch (shape.kind) {
case 'circle': return Math.PI * shape.radius ** 2;
case 'rect': return shape.width * shape.height;
}
}Result type with discriminant
Implements a Result type discriminated on the success boolean, enabling safe access to value or error.
type Ok<T> = { success: true; value: T };
type Err = { success: false; error: string };
type Result<T> = Ok<T> | Err;
function parse(raw: string): Result<number> {
const n = parseFloat(raw);
return isNaN(n)
? { success: false, error: 'Not a number' }
: { success: true, value: n };
}Exhaustive check with never
Adds a never-typed default case so TypeScript errors if a new Action variant is added without a corresponding case.
type Action =
| { type: 'increment'; amount: number }
| { type: 'reset' };
function reduce(count: number, action: Action): number {
switch (action.type) {
case 'increment': return count + action.amount;
case 'reset': return 0;
default:
const _never: never = action;
return count;
}
}
Discussion