thecodex.expert · The Codex Family of Knowledge
TypeScript

Advanced Generics

A generic type parameter can have a default, just like a function parameter can — letting most callers write nothing at all while still allowing full customization when needed.

TypeScript 6.0 Default type parameters Last verified:
Canonical Definition

Generics scale beyond a single type parameter: multiple parameters can reference each other (one constrained by keyof another), a type parameter can have a default so most callers never need to specify it explicitly, and a whole class can be generic, applying its type parameter consistently to every property and method inside it.

Multiple type parameters referencing each other

K is constrained to T's own keys, so TypeScript rejects a call passing a property name that doesn't actually exist on the object being updated.

TypeScriptmultiple_params.ts
function updateProperty<T, K extends keyof T>(obj: T, key: K, value: T[K]): T {
    return { ...obj, [key]: value };
}

const user = { name: "Alice", age: 30 };
updateProperty(user, "age", 31);         // fine
// updateProperty(user, "age", "old");    // COMPILE ERROR: age must be a number
// updateProperty(user, "email", "x");     // COMPILE ERROR: email isn't a key of user

Default type parameters

Most callers can omit the type parameter entirely and get a sensible default, while advanced callers can still override it — the same tradeoff a default function parameter makes.

TypeScriptdefault_type_param.ts
interface ApiResponse<T = unknown> {
    data: T;
    status: number;
}

const generic: ApiResponse = { data: "anything", status: 200 };            // T defaults to unknown
const typed: ApiResponse<{ name: string }> = { data: { name: "Bob" }, status: 200 };  // T explicitly given

Generic classes

The type parameter is fixed once at construction and applies consistently to every method and property in the class, giving a type-safe, reusable container.

TypeScriptgeneric_class.ts
class Box<T> {
    private contents: T;

    constructor(value: T) {
        this.contents = value;
    }

    get(): T {
        return this.contents;
    }

    set(value: T): void {
        this.contents = value;
    }
}

const stringBox = new Box("hello");   // Box<string>, inferred from the constructor argument
const num: number = new Box(42).get();

Sources

1
Microsoft. TypeScript Handbook, "Generics", typescriptlang.org/docs/handbook/2/generics.html.