TypeScript Generics

A practical guide to TypeScript generics, type parameters, constraints, defaults, and reusable data structures.

TL;DR

  1. Declare reusable type placeholders using angle bracket <T> parameter syntax.
  2. Constrain type parameter shapes with the extends keyword operator.
  3. Provide fallback defaults using = in type parameter declarations.

Generic Functions

    type parameter syntax

    Introduces a placeholder type variable within angle brackets.

    function identity<T>(value: T): T {
      return value;
    }
    call-site inference

    Deduces type arguments automatically from passed arguments.

    const text = identity('hello'); // T inferred as string
    const num = identity(42);       // T inferred as number
    multiple type parameters

    Accepts distinct generic variables separated by commas.

    function pair<K, V>(key: K, val: V): [K, V] {
      return [key, val];
    }
    const entry = pair('port', 3000);

Generic Interfaces and Types

    generic interface

    Parameterizes object shapes for versatile data handling.

    interface ApiResponse<T> {
      status: number;
      data: T;
    }
    const res: ApiResponse<string[]> = {
      status: 200,
      data: ['alpha', 'beta']
    };
    generic type alias

    Builds reusable container models with flexible payloads.

    type Result<T, E = Error> =
      | { success: true; value: T }
      | { success: false; error: E };
    generic function signature

    Defines callable generic contracts in interface methods.

    interface Transformer {
      transform<T>(input: T): T;
    }

Type Parameter Constraints

    extends constraint

    Restricts type parameters to shapes matching a contract.

    interface HasId {
      id: string;
    }
    function logId<T extends HasId>(item: T): string {
      return item.id;
    }
    keyof constraint

    Constrains a parameter to valid property keys of another.

    function getProp<T, K extends keyof T>(
      obj: T,
      key: K
    ): T[K] {
      return obj[key];
    }
    primitive constraint

    Limits generics strictly to numbers or string types.

    function add<T extends number>(a: T, b: T): number {
      return a + b;
    }

Default Type Parameters

    default type assignment

    Supplies fallback types when callers omit type arguments.

    type Container<T = string> = {
      content: T;
    };
    const defaultBox: Container = { content: 'text' };
    const numBox: Container<number> = { content: 99 };
    dependent default types

    Derives fallback defaults from preceding type parameters.

    interface CacheEntry<T, K = string> {
      key: K;
      value: T;
    }
    function parameter default

    Assigns default generic types in function declarations.

    function createList<T = number>(): T[] {
      return [];
    }
    const defaultList = createList(); // number[]

Generic Classes

    generic class blueprint

    Shares type parameters across all instance methods.

    class Queue<T> {
      private data: T[] = [];
      push(item: T) { this.data.push(item); }
      pop(): T | undefined { return this.data.shift(); }
    }
    instantiating generic class

    Binds specific types during object creation with new.

    const messages = new Queue<string>();
    messages.push('hello');
    const nextMsg = messages.pop();
    static member limitation

    Static methods cannot reference class type parameters.

    class Stack<T> {
      // static items: T[]; // Error: Cannot reference T
      static createEmpty<V>(): Stack<V> {
        return new Stack<V>();
      }
    }

Tips

  1. Let TypeScript infer generic <T> type arguments from parameter values at call sites whenever possible.
  2. Name type parameters descriptively like TItem or TResponse when complex functions handle multiple distinct generic parameters.

Warnings

  1. Avoid declaring complex <T> generic signatures when a simpler primitive union type fulfills the exact same role.
  2. Remember that type parameters cannot be used as runtime values inside expressions like new T() without constructor signatures.

In Practice

FAQ