snippettypescriptModeratepending
TypeScript discriminated union exhaustive check
Viewed 0 times
discriminated-unionexhaustiveneverswitchtype-safety
Problem
When handling a discriminated union, there's no compile-time guarantee that all cases are covered. Adding a new variant can silently miss handlers.
Solution
Use the never type for exhaustive checking:
type Shape =
| { kind: 'circle'; radius: number }
| { kind: 'square'; side: number }
| { kind: 'triangle'; base: number; height: number };
// Exhaustive helper
function assertNever(x: never): never {
throw new Error(
}
function area(shape: Shape): number {
switch (shape.kind) {
case 'circle': return Math.PI * shape.radius ** 2;
case 'square': return shape.side ** 2;
case 'triangle': return (shape.base * shape.height) / 2;
default: return assertNever(shape); // Compile error if case missing!
}
}
// If you add { kind: 'pentagon'; ... } to Shape,
// the switch will get a compile error at assertNever
// because 'pentagon' doesn't match 'never'.
type Shape =
| { kind: 'circle'; radius: number }
| { kind: 'square'; side: number }
| { kind: 'triangle'; base: number; height: number };
// Exhaustive helper
function assertNever(x: never): never {
throw new Error(
Unexpected value: ${JSON.stringify(x)});}
function area(shape: Shape): number {
switch (shape.kind) {
case 'circle': return Math.PI * shape.radius ** 2;
case 'square': return shape.side ** 2;
case 'triangle': return (shape.base * shape.height) / 2;
default: return assertNever(shape); // Compile error if case missing!
}
}
// If you add { kind: 'pentagon'; ... } to Shape,
// the switch will get a compile error at assertNever
// because 'pentagon' doesn't match 'never'.
Why
The never type represents values that should never occur. If TypeScript can reach assertNever, a case was missed, causing a compile error.
Revisions (0)
No revisions yet.