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"'.