`<T, U>` and the Relationships Between Them
Multiple Type Parameters
Generic functions and types can take multiple parameters. Relationships between them — `<K extends keyof T>` and friends — are where the real expressiveness lives.
What you'll learn
- Use two or more type parameters
- Express relationships between them
- Recognize common shapes
Most useful generics take more than one parameter. The real expressiveness comes from how those parameters relate.
Two Independent Parameters
function pair<A, B>(a: A, b: B): [A, B] {
return [a, b];
}
pair("Ada", 36); // [string, number] Parameters That Relate
function pluck<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
} Three relationships:
K extends keyof T— K is one of T’s keysT[K]— the return type is the property type for that key
This is the pattern behind Object.entries, lodash.get, and
similar.
A Function Like map
function map<T, U>(arr: T[], fn: (item: T, index: number) => U): U[] {
const result: U[] = [];
for (let i = 0; i < arr.length; i++) result.push(fn(arr[i], i));
return result;
}
map([1, 2, 3], n => `n=${n}`); // T = number, U = string → string[] Record<K, V> Style
function fromEntries<K extends string | number, V>(
entries: [K, V][]
): Record<K, V> {
const out = {} as Record<K, V>;
for (const [k, v] of entries) out[k] = v;
return out;
}
fromEntries([["a", 1], ["b", 2]]); // Record<string, number> When To Stop
Two or three type parameters is sweet. Five or more usually means you can simplify — extract types, or split the function.
Up Next
Common generic patterns you’ll see in real code.
Generic Patterns →