Generics

Write functions and types that work with any type while keeping type safety.

function first<T>(arr: T[]): T | undefined {
  return arr[0];
}

first([1, 2, 3]);      // number | undefined
first(["a", "b"]);     // string | undefined

The <T> is a type parameter. TypeScript infers it from the argument, so you rarely need to specify it.

Constrained Generics

Limit what types are accepted:

function getLength<T extends { length: number }>(item: T): number {
  return item.length;
}

getLength("hello");   // OK - strings have length
getLength([1, 2, 3]); // OK - arrays have length
getLength(42);        // Error - numbers don't have length

Generic Interfaces and Types

interface Response<T> {
  data: T;
  status: number;
  error?: string;
}

type UserResponse = Response<User>;
type ListResponse = Response<User[]>;

Multiple Type Parameters

function merge<T extends object, U extends object>(a: T, b: U): T & U {
  return { ...a, ...b };
}

const result = merge({ name: "Alice" }, { age: 30 });
// result: { name: string; age: number }

Tip: Name type parameters meaningfully when you have more than one: <TKey, TValue> is clearer than <K, V> in complex code.