C.W.K.
Stream
Lesson 03 of 07 · published

Generic Types and Interfaces

~9 min · generics, types, interfaces, data-shapes

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
"Generic types are reusable shapes with slots — like a template for a data structure."

Generic interface and type alias

Both interface and type can carry type parameters. interface Box<T> { value: T } defines a Box that wraps any type. type Pair<T> = [T, T] defines a homogeneous pair. The slot is filled when the generic is instantiated: Box<string> is { value: string }; Pair<number> is [number, number].

Generic types are how the standard library expresses Array, Map, Set, Promise, ReturnType, Awaited, and dozens of others. Once you can read Array<T>, you can read almost any library's type definitions.

Multiple parameters

interface Pair<A, B> { first: A; second: B }. type Map<K, V> = .... Two slots, used independently. The compiler infers them when the generic is instantiated from a value.

Where generic types shine

  • Container shapes: Result<T, E> = { ok: true; value: T } | { ok: false; error: E }.
  • Repeated structure: PaginatedResponse<T> = { items: T[]; nextCursor: string | null }.
  • Higher-order shapes: Reducer<S, A> = (state: S, action: A) => S.
  • Library API types: EventHandler<E extends Event> = (e: E) => void.
If you find yourself defining nearly-identical types with one field changing, that field is a slot. Lift it to a generic parameter and the duplication collapses.

Code

Generic shapes — Box and Result·typescript
// Generic interface — Box wraps anything.
interface Box<T> {
  value: T;
  describe(): string;
}

const boxStr: Box<string> = {
  value: 'hi',
  describe() { return `Box(${this.value})` },
};
const boxNum: Box<number> = {
  value: 42,
  describe() { return `Box(${this.value})` },
};

// Generic type alias — Result for fallible operations.
type Result<T, E = Error> =
  | { ok: true; value: T }
  | { ok: false; error: E };

function parseNumber(s: string): Result<number> {
  const n = Number(s);
  return Number.isNaN(n)
    ? { ok: false, error: new Error('not a number') }
    : { ok: true, value: n };
}

External links

Exercise

Define a PaginatedResponse<T> type with items: T[], total: number, and nextCursor: string | null. Then write a fetchUsers(): Promise<PaginatedResponse<User>> signature. Confirm the type system treats it as a paginated response of users specifically.
Hint
Generic types compose with other generics naturally. Promise<PaginatedResponse<User>> is just substituting PaginatedResponse<User> into Promise's slot.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.