Constraints
| Constrain with extends | |
| Keys with keyof | |
| Default type params | |
| Where constraints live |
Constrain with extends
A constraint sets the minimum shape a type argument must have. The extends keyword draws that line. Below, T must carry a length property, so numbers are rejected while strings and arrays pass.
function longest<T extends { length: number }>(a: T, b: T): T {
return a.length >= b.length ? a : b;
}
Keys with keyof
The keyof operator builds a union of the property names of an object type. Pair it with a constraint so the key argument must exist on the object. Typos in property names become compile errors instead of undefined at runtime.
function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
Default type params
A default type argument applies when the caller names no type and inference finds nothing. Write it with an equals sign after the parameter. Defaults keep generic helpers usable with zero angle brackets in the common case.
type Box<T = string> = { value: T };
const a: Box = { value: "hi" };
const b: Box<number> = { value: 3 };
Where constraints live
Constraints belong on the declaration, right after each type parameter, never at the call site. Callers simply pass values and let the checker compare. When a call fails, read the constraint first because it states the exact rule that was broken.
interface Labeled { label: string; }
function printLabel<T extends Labeled>(item: T): void {
console.log(item.label);
}
Next: Return types
Article author: Arthur Isaev