"as const is three features in one keyword: readonly, narrow, tuple. Use it whenever you want the value to be exactly itself."
What as const does
The as const assertion does three things simultaneously to whatever it's applied to:
- Every value is narrowed from its widened type to its literal type.
'red'instead ofstring.42instead ofnumber.trueinstead ofboolean. - Every property becomes readonly. Object properties get a
readonlymodifier; arrays become readonly arrays. - Array literals become tuples (fixed-length, with per-position types) instead of arrays (variable length, with a single element type).
The combined effect: the value's type becomes exactly its shape, with no widening, no mutation, no flexibility. It's the strongest form of "I really mean this exact thing."
When to reach for it
1. Fixed configuration objects. const ROUTES = { CHAT: '/chat', LOGIN: '/login' } as const — every value becomes its literal string type, and the object can't be mutated. If you later type a function parameter as route: typeof ROUTES[keyof typeof ROUTES], you get a literal union of the actual route strings.
2. Fixed lists of constants. const COLORS = ['red', 'green', 'blue'] as const — the array is readonly ['red', 'green', 'blue'], a tuple of literal types. typeof COLORS[number] gives you the literal union 'red' | 'green' | 'blue'.
3. Object literals passed to functions with literal-union parameters. Without as const, { method: 'POST' } infers { method: string }, which won't satisfy a parameter typed { method: 'GET' | 'POST' }. Adding as const makes the property a literal type, and the call type-checks.
as const is the way to say so. If widening is fine (because the value is going to mutate or be reassigned), leave it off.The combined-with-typeof pattern
as const shines when you derive a type from the value. const ROLES = ['admin', 'editor', 'viewer'] as const gives you a readonly tuple. Then type Role = typeof ROLES[number] gives you the literal union 'admin' | 'editor' | 'viewer'. You now have a single source of truth that's both runtime-iterable and type-system-known. This pattern shows up everywhere in TypeScript-aware codebases.
Pippa's confession
as const + typeof X[number] pattern for the brain list, the council mode list, the reasoning levels, and several other small enums-by-convention. The runtime gets a plain array; the type system gets a literal union; both come from one declaration. The pattern is so useful that I'd recommend learning it on day one.