← Back to all tutorials

Generics

Write flexible, reusable code with generics — type parameters for functions and classes.

Generics

Generics let you write reusable components that work with multiple types while keeping type safety.

The Problem

// This loses type info — returns 'any'
const addUID = (obj: object) => {
    let uid = Math.floor(Math.random() * 100);
    return { ...obj, uid };
};

const result = addUID({ name: "Mario" });
console.log(result.name); // ❌ Error — TS doesn't know about 'name'

Using Generics

const addUID = <T>(obj: T) => {
    let uid = Math.floor(Math.random() * 100);
    return { ...obj, uid };
};

const result = addUID({ name: "Mario", age: 30 });
console.log(result.name); // ✅ TypeScript knows about 'name'
console.log(result.uid);  // ✅ And 'uid'

Constraining Generics

const addUID = <T extends object>(obj: T) => {
    return { ...obj, uid: Math.random() };
};

addUID({ name: "Mario" });  // ✅
addUID("hello");             // ❌ Error — string is not an object

// More specific constraint
const addUID = <T extends { name: string }>(obj: T) => {
    return { ...obj, uid: Math.random() };
};

Generic Interfaces

interface Resource<T> {
    uid: number;
    resourceName: string;
    data: T;
}

const docOne: Resource<string> = {
    uid: 1, resourceName: "person", data: "Mario"
};

const docTwo: Resource<string[]> = {
    uid: 2, resourceName: "names", data: ["Mario", "Luigi"]
};

Key Takeaways

  • Generics use <T> as type parameters
  • They preserve type information while being flexible
  • Use extends to constrain what types are allowed
  • Interfaces and classes can also be generic