TypeScript Generics - Questions & Answers

1. What are generics in TypeScript?

Generics: A feature that allows creating reusable components (functions, classes, interfaces) that work with multiple types while maintaining type safety.

Can you give an example of generics in functions and classes?


// Generics in functions and classes
// Generic function
function identity<T>(value: T): T { return value; }

// Generic class
class Box<T> { 
    private content: T; 
    constructor(value: T) { this.content = value; } 
    getContent(): T { return this.content; }
}

// Using generic function
const numberValue = identity<number>(42);
const stringValue = identity<string>("Hello");
console.log(numberValue); // 42
console.log(stringValue); // Hello 

// Using generic class
const numberBox = new Box<number>(100);
const stringBox = new Box<string>("TypeScript");
console.log(numberBox.getContent()); // 100
console.log(stringBox.getContent()); // TypeScript 
        

Output:


42
Hello
100
TypeScript 
        

2. What are generic constraints in TypeScript?

Generic Constraints: Restrict the types that can be used with generics by requiring them to meet certain conditions.

Can you give an example of generic constraints?


// Generic constraints
interface HasLength { length: number; }

function getLength<T extends HasLength>(item: T): number { 
    return item.length; 
}

class Container<T extends { id: number }> { 
    private item: T; 
    constructor(item: T) { this.item = item; } 
    getId(): number { return this.item.id; }
}

// Using constrained generic function
console.log(getLength<string>("TypeScript")); // 10
console.log(getLength<number[]>([1, 2, 3])); // 3
// console.log(getLength<number>(42)); // Error: number has no length 

// Using constrained generic class
const container = new Container<{ id: number, name: string }>({ id: 1, name: "Item" });
console.log(container.getId()); // 1 
        

Output:


10
3
1 
        

3. How do you use multiple generic parameters in TypeScript?

Multiple Generic Parameters: Define multiple type parameters (e.g., <T, U>) to handle different types in a single function or class.

Can you give an example of multiple generic parameters?


// Multiple generic parameters
function merge<T, U>(obj1: T, obj2: U): T & U { 
    return { ...obj1, ...obj2 }; 
}

class Pair<K extends string, V> { 
    private key: K; 
    private value: V; 
    constructor(key: K, value: V) { 
        this.key = key; 
        this.value = value; 
    } 
    getPair(): { key: K, value: V } { 
        return { key: this.key, value: this.value }; 
    }
}

// Using multiple generics in function
const merged = merge<{ name: string }, { age: number }>(
    { name: "kristal" }, 
    { age: 30 }
);
console.log(merged); // { name: "kristal", age: 30 } 

// Using multiple generics in class
const pair = new Pair<"id", number>("user1", 42);
console.log(pair.getPair()); // { key: "user1", value: 42 } 
        

Output:


{ name: "kristal", age: 30 }
{ key: "user1", value: 42 } 
        

4. Can you provide a comprehensive example of generics, constraints, and multiple generic parameters?

Project Structure:


ts-generics/
├── src/
│   └── main.ts
├── tsconfig.json
└── package.json 
        

main.ts:


// Comprehensive generics example
interface Identifiable { id: number; }
interface Describable { description: string; }

// Generic function with constraints
function processItem<T extends Identifiable>(item: T): string { 
    return `Item ID: ${item.id}`; 
}

// Generic class with multiple parameters and constraints
class DataStore<K extends string, V extends Identifiable & Describable> { 
    private store: Map<K, V>; 
    constructor() { this.store = new Map<K, V>(); } 
    add(key: K, value: V): void { this.store.set(key, value); } 
    get(key: K): V | undefined { return this.store.get(key); } 
    describeAll(): string[] { 
        const descriptions: string[] = []; 
        for (const item of this.store.values()) { 
            descriptions.push(`${item.id}: ${item.description}`); 
        } 
        return descriptions; 
    }
}

// Generic function with multiple parameters
function combine<T extends Identifiable, U extends Describable>( 
    item1: T, item2: U
): T & U { 
    return { ...item1, ...item2 }; 
}

// Usage
const item1 = { id: 1, name: "Laptop" };
const item2 = { description: "High-performance device" };

console.log(processItem(item1)); // Item ID: 1 

const store = new DataStore<string, { id: number; description: string }>();
store.add("item1", { id: 1, description: "Item One" });
store.add("item2", { id: 2, description: "Item Two" });
console.log(store.describeAll()); // ["1: Item One", "2: Item Two"] 

const combined = combine( 
    { id: 3 }, 
    { description: "Combined Item" }
);
console.log(combined); // { id: 3, description: "Combined Item" } 
        

package.json:


{
    "name": "ts-generics",
    "version": "1.0.0",
    "scripts": {
        "start": "tsc && node dist/main.js",
        "build": "tsc",
        "watch": "tsc --watch"
    },
    "devDependencies": {
        "typescript": "^5.6.2"
    }
}
        

tsconfig.json:


{
    "compilerOptions": {
        "target": "ES2020",
        "module": "NodeNext",
        "outDir": "./dist",
        "rootDir": "./src",
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": true
    },
    "include": ["src/**/*"],
    "exclude": ["node_modules"]
}
        

Steps to Run:

  1. Create the project structure.
  2. Install dependencies: npm install.
  3. Compile: npm run build.
  4. Run: npm start.

Output:


Item ID: 1
["1: Item One", "2: Item Two"]
{ id: 3, description: "Combined Item" } 
        

Description:

5. What are common mistakes in TypeScript generics?

6. What are best practices for TypeScript generics?

  1. Generics:
    • Use generics for reusable, type-safe components.
    • Specify types explicitly when inference is unclear (e.g., identity<number>(42)).
  2. Generic Constraints:
    • Use precise constraints (e.g., T extends { length: number }) to ensure required properties.
    • Combine interfaces for complex constraints (e.g., T extends Identifiable & Describable).
  3. Multiple Generic Parameters:
    • Use descriptive names (e.g., K for keys, V for values).
    • Constrain each parameter to maintain type safety.
  4. General:
    • Follow TypeScript naming conventions (e.g., T, U for generics).
    • Document generic functions/classes with JSDoc or comments.
    • Test with edge cases (e.g., empty objects, invalid types).
    • Use tsc or linters (e.g., eslint) to adopt type errors early.