TypeScript Fundamentals

Generics

Generics let types depend on other types. They preserve relationships between inputs, outputs, containers, callbacks, and API shapes without giving up reuse.
  • Generics preserve relationships
  • Type parameters should appear more than once
  • Constraints express required capabilities
  • Inference usually fills type arguments
  • Generics can become API surface area
Relationship-preserving generic
function first<T>(items: T[]): T | undefined {
  return items[0]
}

const a = first([1, 2, 3])       // number | undefined
const b = first(["x", "y"])   // string | undefined
`T` connects the array element type to the return type.
Constrained generic
function byId<T extends { id: string }>(items: T[]): Map<string, T> {
  return new Map(items.map((item) => [item.id, item]))
}
The constraint allows `id` while preserving the full item type.
Generic design smells
SmellWhy
T used only onceMay not express a relationship
Too many type parametersHard to infer and explain
Manual type args required oftenAPI may be hard to infer
Generic returns unrelated TLikely unsafe or over-abstract
Sources
  • The TypeScript HandbookGenerics
  • Effective TypeScript, 2nd EditionGenerics
  • TypeScript ReferenceType parameters