TypeScript by Example

Generic Constraints

Constraining type parameters with extends and adding default type parameters for ergonomic APIs.

A constraint tells TypeScript what a generic value must support. The implementation can then use that shape safely.

extends limits T to values with the required members.

function longest<T extends { length: number }>(a: T, b: T): T {
  return a.length >= b.length ? a : b;
}
 
console.log(longest("short", "longer"));
console.log(longest([1, 2], [3, 4, 5]));
 
function withId<T extends object>(item: T, id: string): T & { id: string } {
  return { ...item, id };
}

Default type parameters let callers omit a type argument when a sensible fallback exists.

interface ApiResponse<T = unknown> {
  data: T;
  status: number;
  message: string;
}
 
const raw: ApiResponse = { data: null, status: 200, message: "OK" };
 
const typed: ApiResponse<{ id: string; name: string }> = {
  data: { id: "1", name: "Ada" },
  status: 200,
  message: "OK",
};

In production

Constrain early: <T extends object>, <T extends { id: string }>, or <T extends keyof Model>. A constrained generic is usually clearer than an unconstrained generic followed by casts inside the function.

Enjoyed this? Get more software craft delivered to your inbox.