typescript / expert
Snippet
Literal Preservation through const Type Parameters
The 'const' modifier on a type parameter (TS 5.0+) tells the inference engine to treat the argument as if the caller had written 'as const' — preserving string literals, readonly arrays, and tuple shapes. This eliminates the need to manually annotate every nested literal and unlocks design patterns like type-safe routing tables, state machine definitions, and configuration DSLs where the precise literal values feed downstream conditional types. The caller writes a plain object; the library captures it at maximum precision. Combine with 'satisfies' at the call site when you also want to constrain the structural shape without losing literal narrowing.
snippet.ts
typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function defineRoutes<const T extends Record<string, { path: string; method: "GET" | "POST" }>,>(routes: T): T {return routes;}const api = defineRoutes({users: { path: "/users", method: "GET" },create: { path: "/users", method: "POST" },});// Without 'const T', method would widen to the union "GET" | "POST".// With 'const T', each literal is preserved exactly:type UsersMethod = typeof api.users.method; // "GET"type CreateMethod = typeof api.create.method; // "POST"type RouteNames = keyof typeof api; // "users" | "create"
Breakdown
1
function defineRoutes<const T extends Record<string, ...>>(routes: T): T
'const T' makes inference treat the argument as deeply readonly with literal types preserved.
2
users: { path: "/users", method: "GET" },
Plain object literal — no 'as const' suffix required at the call site.
3
type UsersMethod = typeof api.users.method;
Resolves to "GET", not "GET" | "POST" — each property retains its exact literal.
4
type RouteNames = keyof typeof api;
Yields the union of literal keys, enabling type-safe lookups by name downstream.