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
- 01
const roles = ['admin', 'editor', 'viewer'] as const
The const assertion preserves each element as a literal and makes the tuple readonly.
- 02
type Role = (typeof roles)[number]
`typeof` captures the tuple and `[number]` collects every possible element.
What you get
Role
→"admin" | "editor" | "viewer"RouteName
→"home" | "settings"RoutePath
→"/" | "/settings"
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.
