Menu
Courses / TypeScript Basics / generics

generics

08 / 10 Part of TypeScript Basics

The Problem Generics Solve


Imagine writing a function that returns whatever you pass into it, for any type:


function identity(value: any): any {
    return value;
}

This works, but it loses all type safety — the return type is any, so TypeScript can't help you afterward. Generics solve this by letting a function, class, or interface work with any type while still preserving type information.



A Generic Function


function identity(value: T): T {
    return value;
}

let output1 = identity("Hello"); // output1 is typed as string
let output2 = identity(42);      // output2 is typed as number

T is a type parameter — a placeholder that gets filled in with a real type whenever the function is called. TypeScript can usually infer T automatically:


let output3 = identity("Hello"); // T inferred as string, no need to specify it


Generic Functions with Arrays


function firstElement(arr: T[]): T {
    return arr[0];
}

const firstNum = firstElement([1, 2, 3]);       // number
const firstName = firstElement(["a", "b", "c"]); // string


Multiple Type Parameters


function pair(first: A, second: B): [A, B] {
    return [first, second];
}

const result = pair("age", 30); // ["age", 30]


Generic Interfaces


interface Box {
    contents: T;
}

const stringBox: Box = { contents: "Hello" };
const numberBox: Box = { contents: 42 };


Generic Classes


class DataStore {
    private items: T[] = [];

    add(item: T): void {
        this.items.push(item);
    }

    getAll(): T[] {
        return this.items;
    }
}

const stringStore = new DataStore();
stringStore.add("apple");
stringStore.add("banana");
console.log(stringStore.getAll()); // ["apple", "banana"]


Constraining Generics with extends


Sometimes you want to allow any type, but only if it has certain properties. Use extends to add a constraint.


interface HasLength {
    length: number;
}

function logLength(item: T): void {
    console.log(item.length);
}

logLength("Hello");       // valid, strings have a length property
logLength([1, 2, 3]);     // valid, arrays have a length property
logLength(42);            // Error: number doesn't have a 'length' property


Default Type Parameters


interface Box {
    contents: T;
}

const defaultBox: Box = { contents: "text" }; // T defaults to string


Why Generics Matter



   
   
       
       
       
   
Without GenericsWith Generics
Use any, losing type safetyTypes stay accurate and specific per call
Write duplicate functions for each typeWrite one flexible, reusable function
No autocomplete or type checking after the factFull autocomplete and error checking preserved



    Coming up next: Union and intersection types, plus type narrowing — handling values that could be more than one type.