Generic Interfaces and Classes

Generic Interfaces

Example

A generic interface describes a container whose content type is decided by the user.

interface Page<T> {
  items: T[];
  page: number;
  totalPages: number;
}

type Product = { id: number; name: string };

const productPage: Page<Product> = {
  items: [{ id: 1, name: "Kettle" }, { id: 2, name: "Toaster" }],
  page: 1,
  totalPages: 5,
};

const namePage: Page<string> = { items: ["a", "b"], page: 2, totalPages: 2 };
console.log(productPage.items.map((p) => p.name), namePage.items.length);

Output

[ 'Kettle', 'Toaster' ] 2

Generic Classes

Example

A typed in-memory repository that works for any entity with an id. This is how repository layers in NestJS and TypeORM are typed.

class InMemoryRepo<T extends { id: number }> {
  private rows = new Map<number, T>();

  save(row: T): T {
    this.rows.set(row.id, row);
    return row;
  }
  findById(id: number): T | undefined {
    return this.rows.get(id);
  }
  findAll(): T[] {
    return [...this.rows.values()];
  }
}

type Product = { id: number; name: string; price: number };
type User = { id: number; email: string };

const products = new InMemoryRepo<Product>();
products.save({ id: 1, name: "Kettle", price: 1299 });
products.save({ id: 2, name: "Toaster", price: 1899 });

const users = new InMemoryRepo<User>();
users.save({ id: 7, email: "[email protected]" });

console.log(products.findAll().map((p) => p.price));
console.log(users.findById(7)?.email, users.findById(8));

Output

[ 1299, 1899 ]
[email protected] undefined