You constrain your type parameters with extends and provide defaults to build ergonomic generic APIs.
Open this lesson in KodokonAn unconstrained generic is opaque: inside the function body, T lets you do nothing. The extends clause establishes a minimal contract on the type parameter, which unlocks access to the guaranteed properties while preserving the caller's precise type. That is the fundamental difference from a parameter typed with an interface: the constraint keeps the information, the interface erases it.
function longest<T extends { length: number }>(
a: T,
b: T
): T {
return a.length >= b.length ? a : b;
}
const word = longest("hello", "worldwide");
const list = longest([1, 2], [3, 4, 5]);The K extends keyof T duo is the most profitable everyday pattern: it links two type parameters together and statically guarantees that a key actually belongs to the object. The T[K] return type (an indexed access type) precisely tracks the requested property. This is exactly how functions like pick or pluck from utility libraries are typed.
function getProp<T extends object, K extends keyof T>(
obj: T,
key: K
): T[K] {
return obj[key];
}
const config = { port: 8080, host: "localhost" };
const port = getProp(config, "port");
const host = getProp(config, "host");Defaults (T = string) make a generic optional at the call site: users only provide the type argument when they want to deviate from the common case. Combined with a constraint (T extends X = Y), they define both the bound and the implicit behavior. The default must satisfy the constraint, otherwise the declaration itself does not compile. This is the standard pattern for HTTP clients, stores, and typed event emitters.
interface ApiResponse<T = unknown> {
status: number;
data: T;
}
async function fetchJson<T = unknown>(
url: string
): Promise<ApiResponse<T>> {
const res = await fetch(url);
return { status: res.status, data: await res.json() };
}
interface Post { id: number; title: string }
const typed = await fetchJson<Post>("/api/posts/1");
const loose = await fetchJson("/api/health");K extends keyof T guarantee in a generic signature?longest<T extends { length: number }> have over longest(a: { length: number }, b: ...) without generics?<T = unknown> over <T = any> as a default value?