Generic Constraints

`<T extends X>` — "T Must Be Some Kind of X"

Generic Constraints

An unconstrained generic accepts anything. `extends` restricts T to a known shape — so you can use its properties.

4 min read Level 2/5 #typescript#generics#constraints
What you'll learn
  • Constrain a generic with `extends`
  • Use `keyof` in a constraint
  • Recognize common constraint patterns

An unconstrained <T> can be anything — so you can only do things that work for ANY type. To use more, constrain T.

The Problem

function logLength<T>(x: T) {
  console.log(x.length);
  //            ~~~~~~ Property 'length' does not exist on type 'T'.
}

T could be a number, a boolean, a function — none of which have length.

The Fix — extends

function logLength<T extends { length: number }>(x: T) {
  console.log(x.length);   // ✓ — T is guaranteed to have .length
}

logLength("hello");         // ✓ string has length
logLength([1, 2, 3]);       // ✓ array has length
logLength({ length: 5 });   // ✓ object with length
logLength(42);              // ✗ number has no length

extends X says “T must be assignable to X”. You get to use X’s properties on T.

Why Not Just Use the Constraint As the Type?

You could write:

function logLength(x: { length: number }) {
  console.log(x.length);
}

The difference: the generic version PRESERVES the input type for returns. Compare:

function trim1<T extends { length: number }>(x: T): T { return x; }
function trim2(x: { length: number }): { length: number } { return x; }

const a = trim1("hello");   // string
const b = trim2("hello");   // { length: number }  — loses the string type

If you need the type to flow through, generics with constraints.

Constraining with keyof

The classic pattern — type-safe property access:

function pluck<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = { name: "Ada", age: 36 };

pluck(user, "name");    // string
pluck(user, "age");     // number
pluck(user, "bogus");   // ✗ Argument of type '"bogus"' is not assignable...

K extends keyof T constrains K to be one of T’s actual keys. The return type T[K] is the type of that property. We cover keyof in depth in the advanced types chapter.

Common Constraint Shapes

ConstraintWhen
T extends stringAny string subtype, including literals
T extends numberAny number
T extends objectAny object (not a primitive)
T extends { foo: ... }Any value with that property
T extends keyof UA property name of U
T extends readonly unknown[]A tuple or array

Default Type Arguments

When constraints make a parameter optional in practice:

function box<T = string>(value: T): { value: T } {
  return { value };
}

box(1);          // T = number
box<string>();   // ✗ value required — but type can be omitted

We cover defaults in the next-but-one lesson.

Up Next

Naming generic types — type aliases that take parameters.

Generic Types →