`<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.
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
| Constraint | When |
|---|---|
T extends string | Any string subtype, including literals |
T extends number | Any number |
T extends object | Any object (not a primitive) |
T extends { foo: ... } | Any value with that property |
T extends keyof U | A 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 →