โ† TypeScript Foundations

Generics

~320 words ยท 2 min read

Generics in TypeScript

Generics let you write code that works across a range of types while preserving type safety. They are the key to reusable, type-correct functions and data structures.

The Problem Generics Solve

Without generics, a function that returns its input is either untyped (any) or duplicated for every type. A generic captures the type once and carries it through the function.

function identity<T>(value: T): T {
    return value;
}
const n = identity(42);    // n is number
const s = identity("hi");  // s is string

Generic Functions

The type parameter T (or any name) is a placeholder filled in at the call site. TypeScript usually infers it from the argument.

function first<T>(arr: T[]): T | undefined {
    return arr[0];
}
first([1, 2, 3]);     // number | undefined
first(["a", "b"]);    // string | undefined

Generic Constraints

Sometimes a generic must satisfy a contract. The extends keyword constrains the type parameter so you can safely access specific properties.

function getLength<T extends { length: number }>(x: T): number {
    return x.length;   // OK: T is guaranteed to have length
}
getLength("hello");    // 5
getLength([1, 2, 3]);  // 3

Constraints turn a loose generic into a precise contract. <T extends { length: number }> says "any type T, as long as it has a numeric length property" โ€” safe and flexible at once.

Utility Types

TypeScript ships built-in generic types that transform other types. The most common are Partial, Pick, and Omit.

interface User {
    id: number;
    name: string;
    email: string;
}
type UserPatch = Partial<User>;            // every property optional
type UserName = Pick<User, "id" | "name">;  // only id and name
type PublicUser = Omit<User, "email">;      // drops email

Partial<T> makes every property optional; Pick<T, K> selects a subset of keys; Omit<T, K> removes keys. Together they let you derive new types from existing ones without repetition.