Careeroza
Generic Constraints
Limiting T with extends
<T extends Something> means "T can be any type, as long as it has at least the shape of Something". Inside the function you can then use those members safely.
Example
The function returns T, not just { price: number }, so callers keep all their own fields.
function cheapest<T extends { price: number }>(items: T[]): T {
return items.reduce((min, item) => (item.price < min.price ? item : min));
}
const phones = [
{ name: "Phone A", price: 15999, ram: 8 },
{ name: "Phone B", price: 12999, ram: 6 },
];
const best = cheapest(phones);
console.log(best.name, best.ram);Output
Phone B 6
Violating the Constraint
Example
Objects without a numeric price are rejected (here TS2353: name does not exist in type { price: number }).
function cheapest<T extends { price: number }>(items: T[]): T {
return items[0];
}
cheapest([{ name: "Free sample" }]);Compiler output (tsc)
Line 4: error TS2353: Object literal may only specify known properties, and 'name' does not exist in type '{ price: number; }'.keyof Constraint
K extends keyof T limits K to the property names of T. Combined with the indexed access type T[K], the return type follows the key you pass.
Example
function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
function pluck<T, K extends keyof T>(rows: T[], key: K): T[K][] {
return rows.map((r) => r[key]);
}
const product = { id: 1, name: "Kettle", price: 1299, inStock: true };
const price = getProp(product, "price"); // number
const inStock = getProp(product, "inStock"); // boolean
console.log(price + 1, inStock);
const rows = [product, { id: 2, name: "Toaster", price: 1899, inStock: false }];
console.log(pluck(rows, "name"));Output
1300 true [ 'Kettle', 'Toaster' ]
Wrong Key
A key that does not exist is rejected (TS2345), and the editor autocompletes valid keys.
function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
getProp({ id: 1, name: "Kettle" }, "price");Compiler output (tsc)
Line 4: error TS2345: Argument of type '"price"' is not assignable to parameter of type '"id" | "name"'.