Multiple Type Parameters

`<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.

3 min read Level 2/5 #typescript#generics#multiple-params
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 keys
  • T[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 →