Discriminated Unions
Model finite variants with a shared discriminant field — switch exhaustiveness, never, and type-safe handlers for API and UI state.
Discriminated unions attach a shared literal field — status, kind, or type — to each variant. Comparing that field narrows the whole object, unlocking autocomplete for variant-specific properties. This models API responses, reducer actions, and async UI state without class hierarchies.
Each variant carries only relevant fields — success has data, error has message, loading has neither. Optional-everything objects let invalid combinations type-check; tagged unions make impossible states unrepresentable at compile time.
Defining tagged variants
Generic success variants AsyncState<T> reuse the same status machine for any payload type. Consumers switch on status and get precise fields per branch without manual casts.
Hello, AdaExhaustiveness with never
Assigning the remaining shape to never in default proves every variant is handled. Adding a new kind without a case makes default unreachable with a type error — a compile-time safety net for growing unions.
Result types and dispatch
The ok discriminant from lesson 5 is the simplest discriminated union — boolean tag with mutually exclusive payloads. Dispatch functions switch on channel or type for finite notification variants.
| Pattern | Discriminant |
|---|---|
| Async UI | status |
| API result | ok |
| Reducer action | type |
When not to use tagged unions
Open-ended JSON or plugin architectures may not fit closed variant sets — use unknown plus validation instead. For three-state booleans, a literal union on one field beats three optional booleans.
When variants grow past a dozen, consider splitting into separate types per domain area or using a registry map keyed by discriminant — long switch statements become maintenance burden even when exhaustiveness checks pass.
- Shared literal field tags variants
- switch narrows all branches
- never catches missing cases
- 1Tag variants with a literal discriminant
- 2Switch for exhaustiveness
- 3Prefer tags over optional everything
- 4Next: interfaces and type aliases