Declare constants with as const, and satisfies when a type exists
Declare constant objects and arrays with as const, so their values keep literal types and become readonly.
When the constant must match an existing type, add satisfies to check it without widening.
Implementation
- Use
as conston objects and arrays that never change, such as allowed roles or default settings. - Use
as const satisfies Typewhen a type describes what the constant must contain, such as a union of allowed roles or a generated API type. - Prefer
satisfiesover a type annotation for constants; an annotation widens the value to the annotated type and loses the literal types. - Derive types from the constant when the constant is the source of truth, such as
type Role = (typeof ROLES)[number].
Rationale
Without as const, ['admin', 'editor'] is string[], so the compiler cannot tell which values are allowed, and code can push to it.
An annotation such as : ReadonlyArray<UserRole> checks the values but widens the type to the whole union.
as const satisfies keeps the exact values, makes them readonly, and still checks them against the type.
Examples
These examples use the following types:
type UserRole = 'admin' | 'editor' | 'viewer';
type OrderStatus = { pending: 'pending' | 'idle'; fulfilled: boolean };
Incorrect (counterexample):
const DASHBOARD_ROLES = ['admin', 'editor'];
const DEFAULT_ORDER: OrderStatus = { pending: 'idle', fulfilled: true };
DASHBOARD_ROLES is string[] and mutable, and DEFAULT_ORDER.pending is widened to 'pending' | 'idle'.
Correct:
const DASHBOARD_ROLES = ['admin', 'editor'] as const satisfies ReadonlyArray<UserRole>;
// readonly ['admin', 'editor']
const DEFAULT_ORDER = { pending: 'idle', fulfilled: true } as const satisfies OrderStatus;
// { readonly pending: 'idle'; readonly fulfilled: true }
A typo such as 'editr' fails the satisfies check.
Validation
Hover over constants in the editor and check that they have literal, readonly types.
Check that constants meant to match a type use satisfies rather than an annotation.
A mutable value that is deliberately changed at runtime is not a constant and needs no as const.