TypeScript generics: the useful 20 percent

You do not need advanced type gymnastics to get most of the value. Start with relationships between inputs and outputs.

TypeScript generics: the useful 20 percent cover illustration

Generics are often taught as a way to avoid repeating types. That is true, but the deeper value is preserving a relationship. A function that returns the first item should return the same type it received, not a widened unknown.

generic.tsTypeScript
function first<T>(items: T[]): T | undefined {
  return items[0]
}

function byId<T extends { id: string }>(items: T[], id: string): T | undefined {
  return items.find((item) => item.id === id)
}

const named = byId([{ id: 'html', title: 'HTML' }], 'html')
// named?.title is available without a cast

The two constraints worth learning first

  • Use a constraint when the function needs a capability: T extends { id: string } says the value has an id.
  • Use keyof when the function needs a property name: K extends keyof T.
  • If a generic appears only once and has no constraint, a concrete type is probably clearer.

Do not start with conditional types and recursive inference. Start with a function you would otherwise write twice. The compiler will tell you when the relationship is not actually preserved.

Something to add?

Corrections and technical review are welcome. Tell us what we missed.

Send a correction