🔀

Discriminated Unions — The Pattern Used Instead of Enums in TypeScript

Branch on a type field and TypeScript auto-narrows types in each branch

Branch on shape.kind and TypeScript auto-narrows shape's type inside each case.

Why Instead of Enums

Enums generate runtime code (reverse mapping object). Discriminated unions exist only at type level — zero runtime cost.

Exhaustive Check

Use never type to verify all cases handled at compile time. Same effect as Rust's exhaustive match.

Key Points

1

Add a common literal field (kind, type) to each union member = discriminant

2

switch/if on discriminant → TypeScript auto-narrows types

3

never type for exhaustive check — compile error on missing cases

4

Zero runtime cost vs enum + equal or better type safety

Use Cases

Redux actions — { type: "INCREMENT" } | { type: "SET_VALUE", payload: number } API responses — { status: "success", data: T } | { status: "error", message: string }