Generics
The problem generics solve
Section titled “The problem generics solve”Suppose you want a function that returns whatever you give it. Without generics you face a bad choice:
// Option A: one type only — not reusablefunction identityStr(x: string): string { return x; }
// Option B: any — reusable, but throws away all type informationfunction identityAny(x: any): any { return x; }
const a = identityAny(42);a.toUpperCase(); // 💥 no error at compile time — any hides the bugany makes the function reusable but blinds the compiler: the link between the input type and the output type is gone. Generics keep that link.
function identity<T>(x: T): T { return x; }
const n = identity(42); // T inferred as number → n: numberconst s = identity("hi"); // T inferred as string → s: stringn.toUpperCase(); // ❌ Error: number has no toUpperCase — the bug is caughtT is a type parameter — a placeholder the caller (or inference) fills in. The function works for every type, and the return type still tracks the argument type exactly.
What generics really are
Section titled “What generics really are”A generic is a function at the type level: it takes types as input and produces types as output. identity<T> says “for any type T, take a T and return a T.” This is called parametric polymorphism — one implementation, parameterized over types.
flowchart LR impl["one generic identity of T"] --> callN["called with number returns number"] impl --> callS["called with string returns string"] impl --> callU["called with User returns User"]
What this module covers
Section titled “What this module covers”| Lesson | What you’ll learn |
|---|---|
| Functions & constraints | Type parameters, extends constraints, default type parameters |
| Classes & interfaces | Generic classes, interfaces, and per-method type parameters |
| Conditional types | T extends U ? X : Y and distribution over unions |
Inference & infer | How type arguments are inferred, and extracting types with infer |