Skip to content
Generics
step 1/5

Reading — step 1 of 5

Learn

~1 min readGenerics and Unions

Generics parameterize types over types. Most useful when a function or class works with values whose types you don't want to fix:

function firstOrDefault<T>(list: T[], fallback: T): T {
    return list.length > 0 ? list[0] : fallback;
}

const n = firstOrDefault([1, 2, 3], 0);             // T inferred as number
const s = firstOrDefault<string>([], "default");    // T explicit

Generic classes:

class Box<T> {
    constructor(public value: T) {}
    map<U>(f: (x: T) => U): Box<U> {
        return new Box(f(this.value));
    }
}

const b = new Box(5).map(n => n * 2).map(n => `value: ${n}`);
// Box<string> { value: 'value: 10' }

Constraints — restrict T to types with certain shape:

interface Lengthy { length: number; }

function longest<T extends Lengthy>(a: T, b: T): T {
    return a.length >= b.length ? a : b;
}

longest("hello", "hi");        // string is Lengthy ✓
longest([1, 2, 3], [4]);        // arrays have length ✓
// longest(5, 10);              // number doesn't have length — compile error

Multiple type parameters:

function pair<A, B>(a: A, b: B): [A, B] {
    return [a, b];
}

Generics are erased at runtime — they exist only at compile time. Same as Java's type erasure.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…