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.

6 min read

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:

typescripttypescript
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:

typescripttypescript
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:

typescripttypescript
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 needed

The 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:

typescripttypescript
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:

typescripttypescript
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:

typescripttypescript
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

Rune AI

Key Insights

  • const T infers the most specific type for a generic parameter.
  • Added in TypeScript 5.0.
  • Eliminates the need to write as const at call sites.
  • Only affects values written inline at the call site, not variables passed by reference.
  • Pair with readonly constraints for best results.
RunePowered by Rune AI

Frequently Asked Questions

What does const on a type parameter do?

It tells TypeScript to infer the most specific type for that parameter, the same way `as const` does on a value. Instead of inferring `string[]` for `['a', 'b']`, it infers `readonly ['a', 'b']`.

Does const T require an immutable constraint?

No. The const modifier only affects inference, not assignability. But using a mutable constraint like `string[]` may cause inference to fall back to the constraint since `readonly` arrays are not assignable to mutable ones.

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.