Const Type Parameters in TypeScript
The const modifier on type parameters tells TypeScript to infer the most specific type, like as const but automatic. Added in TypeScript 5.0.
TypeScript const type parameters tell TypeScript to infer the most specific type possible for a generic argument, the same way as const does for values. They were added in TypeScript 5.0.
The problem it solves is common. Consider a function that returns the names from an object:
type HasNames = { names: readonly string[] };
function getNamesExactly<T extends HasNames>(arg: T): T["names"] {
return arg.names;
}
const names = getNamesExactly({ names: ["Alice", "Bob", "Eve"] });
// names is string[], not readonly ["Alice", "Bob", "Eve"]TypeScript infers the type of names as string[]. You probably wanted the most specific type, readonly ["Alice", "Bob", "Eve"], instead.
Before TypeScript 5.0, the fix was adding as const at every call site:
const names = getNamesExactly({ names: ["Alice", "Bob", "Eve"] } as const);
// names is readonly ["Alice", "Bob", "Eve"]This is easy to forget and adds noise at every call. The const modifier on the type parameter makes this the default:
function getNamesExactly<const T extends HasNames>(arg: T): T["names"] {
return arg.names;
}
const names = getNamesExactly({ names: ["Alice", "Bob", "Eve"] });
// names is readonly ["Alice", "Bob", "Eve"] -- no as const neededThe const before T tells TypeScript to infer the narrowest possible type for that parameter. The result is the same as if every caller wrote as const, but the caller does not need to remember.
How const inference works
When a type parameter has the const modifier, TypeScript applies const-like inference to the argument. For objects, properties become readonly and literal types are preserved.
For arrays, tuples are inferred instead of plain arrays. For primitives, literal types are used.
This only affects arguments written inline at the call site. If you pass a variable that was already declared without as const, the const modifier has no effect:
const arr = ["a", "b", "c"];
// arr is string[]
const names = getNamesExactly({ names: arr });
// names is still string[], because arr was already inferred as string[]The const modifier narrows during inference, not during assignment of already-declared variables.
Using readonly constraints
The const modifier does not change assignability rules. If your constraint uses a mutable type, inference may fall back to the constraint when the readonly inferred type does not match:
declare function bad<const T extends string[]>(args: T): void;
bad(["a", "b", "c"]);
// T is still string[] because readonly ["a", "b", "c"] is not assignable to string[]The inferred candidate is readonly ["a", "b", "c"], but a readonly array cannot be assigned to a mutable string array constraint. TypeScript falls back to the constraint, and the const modifier has no effect.
The fix is simple: use readonly in your constraints:
declare function good<const T extends readonly string[]>(args: T): void;
good(["a", "b", "c"]);
// T is readonly ["a", "b", "c"]This is a good practice for any generic that accepts arrays or objects without mutating them. For more on readonly patterns, see Readonly Arrays in TypeScript.
When to use const type parameters
Use const type parameters when the generic function should preserve the exact types that callers pass, such as in builder APIs, configuration helpers, or type-safe parsers where knowing the literal values enables better downstream inference.
If the function always widens its argument or does not benefit from specific types, skip the const modifier. It adds complexity without benefit for simple utilities like firstElement(arr) where string[] is perfectly adequate.
Relationship to satisfies and as const
The const modifier on type parameters complements the as const assertion and the satisfies operator.
as const is the per-call-site tool. Const type parameters make specificity the default for a function.
Satisfies validates without changing inference. For more on satisfies, see Satisfies Operator in TypeScript.
All three tools address the same tension. TypeScript's default inference favors generality, but many APIs benefit from specificity. You now have three ways to opt into specificity, depending on whether you control the function signature, the call site, or both.
Rune AI
Key Insights
const Tinfers the most specific type for a generic parameter.- Added in TypeScript 5.0.
- Eliminates the need to write
as constat call sites. - Only affects values written inline at the call site, not variables passed by reference.
- Pair with
readonlyconstraints for best results.
Frequently Asked Questions
What does const on a type parameter do?
Does const T require an immutable constraint?
Conclusion
Const type parameters eliminate the need to add as const at every call site when you want specific type inference. Add const before a type parameter name, and TypeScript infers the narrowest possible type automatically. Use readonly constraints to avoid surprising fallback behavior.
More in this topic
TypeScript Compiler Explained
The TypeScript compiler (tsc) turns .ts files into .js files while checking types. Learn what it does, how to use it, and how it fits your workflow.
Set Up ESLint for TypeScript
Install and configure ESLint with typescript-eslint to catch bugs and enforce consistent code style in your TypeScript project.
Pick Utility Type in TypeScript
Pick creates a new type by selecting only the properties you name from an existing type. Use it to build focused types for specific views, API responses, or function parameters.