"Generic types are reusable shapes with slots — like a template for a data structure."
Generic interface and type alias
Both interface and type can carry type parameters. interface Box<T> { value: T } defines a Box that wraps any type. type Pair<T> = [T, T] defines a homogeneous pair. The slot is filled when the generic is instantiated: Box<string> is { value: string }; Pair<number> is [number, number].
Generic types are how the standard library expresses Array, Map, Set, Promise, ReturnType, Awaited, and dozens of others. Once you can read Array<T>, you can read almost any library's type definitions.
Multiple parameters
interface Pair<A, B> { first: A; second: B }. type Map<K, V> = .... Two slots, used independently. The compiler infers them when the generic is instantiated from a value.
Where generic types shine
- Container shapes:
Result<T, E> = { ok: true; value: T } | { ok: false; error: E }. - Repeated structure:
PaginatedResponse<T> = { items: T[]; nextCursor: string | null }. - Higher-order shapes:
Reducer<S, A> = (state: S, action: A) => S. - Library API types:
EventHandler<E extends Event> = (e: E) => void.
If you find yourself defining nearly-identical types with one field changing, that field is a slot. Lift it to a generic parameter and the duplication collapses.