Discriminated Union
Problem
State objects contain mutually exclusive combinations of fields, leading to impossible states like having both error and success data simultaneously. Developers must remember which fields are valid together, making null checks scattered throughout the code. When handling different states, TypeScript can’t verify all cases are covered, allowing unhandled states to slip through.
Solution
Use a common property to differentiate between variant types in a union, enabling exhaustive type checking. This lets the compiler verify you’ve handled every case and provides accurate autocomplete for variant-specific properties.
Example
This example demonstrates a discriminated union using a status field to safely differentiate between loading, success, and error states.
// Define mutually exclusive state variants with a discriminant property
type Result =
| { status: 'loading' } // Loading state has no data
| { status: 'success'; data: User } // Success state has data
| { status: 'error'; error: string }; // Error state has error message
function render(result: Result) {
// TypeScript narrows the type based on status discriminant
switch (result.status) {
case 'loading': return <Spinner />;
case 'success': return <User data={result.data} />; // TypeScript knows data exists
case 'error': return <Error message={result.error} />; // TypeScript knows error exists
}
}
Benefits
- Prevents impossible states by making variants mutually exclusive.
- Enables exhaustive type checking to ensure all cases are handled.
- Provides accurate autocomplete for variant-specific properties.
- Makes state transitions explicit and type-safe.
- Compiler catches missing case handling at build time.
Tradeoffs
- Requires understanding of TypeScript’s type system and unions.
- Can result in verbose type definitions for complex state machines.
- Adds boilerplate for discriminating property on every variant.
- May be overkill for simple boolean flags or two-state systems.
- Requires careful design upfront to model all possible states.