Kodokon kodokon.com

Generics: simple generic functions and types

Write reusable functions and types that keep full type precision on every call.

9 min · 3 questions

Open this lesson in Kodokon

You already use generics without realizing it: string[], Promise<number>... Time to write your own. A generic introduces a type parameter, a variable replaced by a concrete type at each use. The goal: write the logic once, without sacrificing type precision.

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

const n = first([10, 20, 30]);
const s = first(["a", "b"]);
T is inferred: n is number | undefined, s is string | undefined

Notice that you didn't specify anything at the call site: TypeScript infers T from the argument. An unconstrained generic accepts anything; yet a function often needs a minimum of guarantees about its data. The extends keyword imposes a minimal shape on T.

TYPESCRIPT
interface HasId {
  id: number;
}

function findById<T extends HasId>(
  items: T[],
  id: number
): T | undefined {
  return items.find((item) => item.id === id);
}
Constraint: T must at least have a numeric id

Types themselves can be generic. This is everyday life in network code: the response structure is stable, only the content of data varies. A single type then describes all your API responses.

TYPESCRIPT
interface ApiResponse<T> {
  ok: boolean;
  data: T;
}

type UserResponse = ApiResponse<{ name: string }>;

const res: UserResponse = {
  ok: true,
  data: { name: "Ada" },
};
A generic type parameterizes the content of data

Knowledge check

Make sure you remember the key points of this lesson.

  1. In function first<T>(items: T[]): T | undefined, what does T represent?
    • A concrete type defined once and for all
    • A type parameter replaced at each call
    • The unknown type
    • A shorthand for any
  2. What does the constraint <T extends HasId> guarantee?
    • T is exactly the HasId type
    • T has at least the properties of HasId
    • T inherits from the HasId class
  3. You call first([10, 20, 30]) without specifying a type. What is T?
    • any
    • unknown
    • number
    • 10 | 20 | 30