TypeScript by Example

Generics

Type parameters for functions, interfaces, and classes that preserve the caller's exact type.

Generics let one implementation work across types while preserving type safety. The caller usually determines the type argument through inference.

A generic function accepts a type parameter in angle brackets.

function identity<T>(value: T): T {
  return value;
}
 
const s = identity("hello"); // string
const n = identity(42); // number
 
function first<T>(arr: T[]): T | undefined {
  return arr[0];
}

Generic interfaces keep structures consistent for whichever type the caller chooses.

interface Stack<T> {
  push(item: T): void;
  pop(): T | undefined;
  readonly size: number;
}
 
class ArrayStack<T> implements Stack<T> {
  private items: T[] = [];
  push(item: T): void {
    this.items.push(item);
  }
  pop(): T | undefined {
    return this.items.pop();
  }
  get size(): number {
    return this.items.length;
  }
}
 
const stack = new ArrayStack<number>();
stack.push(1);

In production

Generics are how libraries keep caller-specific types instead of collapsing everything to unknown or any. If your generic body needs a property, add a constraint in the next lesson rather than casting.

Enjoyed this? Get more software craft delivered to your inbox.