Skip to content
Derive a union from runtime data

    ↑↓ move · ⏎ open · esc close

    Patterns

    How do I keep a literal union and the array or object used at runtime from drifting apart?

    Keep one value as the source of truth, preserve its literals with `as const`, then query its type.

    The recipe

    const roles = ['admin', 'editor', 'viewer'] as const
    type Role = (typeof roles)[number]
    
    const routes = {
      home: '/',
      settings: '/settings',
    } as const
    
    type RouteName = keyof typeof routes
    type RoutePath = (typeof routes)[RouteName]

    The build compiles this and checks each result below.

    How it works

    1. 01
      const roles = ['admin', 'editor', 'viewer'] as const

      The const assertion preserves each element as a literal and makes the tuple readonly.

    2. 02
      type Role = (typeof roles)[number]

      `typeof` captures the tuple and `[number]` collects every possible element.

    What you get

    Where it goes wrong

    `as const` does not freeze the runtime object. It narrows the static view, so external or mutable data still needs validation and possibly an actual freeze.

    Takeaway

    Define literals once as data and derive their union; never maintain the same list in value and type space by hand.

    See also

    Patterns