Skip to content

Generics#

Generic Function (Single Parameter)#

function identity<T>(arg: T): T {
  return arg;
}

const num = identity(42);
const str = identity("hello");

Generic Function (Arrays)#

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

const n = getFirst([1, 2, 3]);
const s = getFirst(["a", "b"]);

Generic Function (Multiple Parameters)#

function pair<T, U>(first: T, second: U): [T, U] {
  return [first, second];
}

const p = pair(1, "one");

Generic Constraints (extends)#

function logLength<T extends { length: number }>(item: T): void {
  console.log(item.length);
}

logLength("hello");
logLength([1, 2]);
// logLength(123); // Error

Generic Constraints (keyof)#

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

const user = { name: "Alice", age: 30 };
const age = getProp(user, "age");

Generic Interfaces#

interface Box<T> {
  value: T;
}

const box: Box<number> = { value: 10 };

Generic Classes#

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

const q = new Queue<string>();
q.push("task");

Generic Type Aliases#

type Result<T> = { success: true; data: T } | { success: false; error: string };

const r: Result<number> = { success: true, data: 42 };

Conditional Types#

type IsString<T> = T extends string ? true : false;

type A = IsString<string>; // true
type B = IsString<number>; // false

Mapped Types#

type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

interface User { id: number; name: string; }
type ReadonlyUser = Readonly<User>;

Generic API Response Wrapper#

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

async function fetchUser(): Promise<ApiResponse<{ name: string }>> {
  return { data: { name: "Alice" }, status: 200 };
}

Generic Repository Class#

class Repository<T extends { id: number }> {
  private items: T[] = [];

  add(item: T) { this.items.push(item); }

  findById(id: number): T | undefined {
    return this.items.find(x => x.id === id);
  }
}

Generic Event Emitter#

class EventEmitter<T extends Record<string, any>> {
  emit<K extends keyof T>(event: K, data: T[K]) { /* ... */ }
  on<K extends keyof T>(event: K, cb: (data: T[K]) => void) { /* ... */ }
}

type Events = { login: { user: string }; logout: void };
const bus = new EventEmitter<Events>();
bus.emit("login", { user: "Alice" });