Frontend Patterns

Pattern

Discriminated Union

Use tagged unions to model mutually exclusive state variants safely.

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.
Stay Updated

Get New Patterns
in Your Inbox

Join thousands of developers receiving weekly insights on frontend architecture patterns

No spam. Unsubscribe anytime.