Skip to main content

Understanding Generics

Generics allow you to write flexible, reusable code that works with multiple types while maintaining type safety. They're like templates for types.

Why Use Generics?

Without generics, you'd need to write the same code multiple times for different types:

function getFirstNumber(arr: number[]): number {
return arr[0];
}

function getFirstString(arr: string[]): string {
return arr[0];
}

With generics, you write it once:

function getFirst<T>(arr: T[]): T {
return arr[0];
}

// Usage
const firstNum = getFirst([1, 2, 3]); // Type: number
const firstStr = getFirst(["a", "b", "c"]); // Type: string

Generic Interfaces

Interfaces can also use generics, commonly seen in data containers:

interface Container<T> {
value: T;
getValue(): T;
}

// Implementation
class NumberContainer implements Container<number> {
constructor(public value: number) {}

getValue(): number {
return this.value;
}
}

Generic Constraints

You can limit what types can be used with your generic code:

interface HasLength {
length: number;
}

function logLength<T extends HasLength>(item: T): void {
console.log(item.length);
}

// Valid
logLength("hello"); // String has length
logLength([1, 2, 3]); // Array has length
// Invalid
logLength(123); // Error: number doesn't have length

Multiple Type Parameters

Generics can work with multiple types:

function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}

const result = pair("hello", 42); // Type: [string, number]

Generic Type Defaults

You can provide default types for your generics:

interface ApiResponse<T = any> {
data: T;
status: number;
message: string;
}

// Uses default type
const response1: ApiResponse = {
data: "any data",
status: 200,
message: "Success"
};

// Specifies type
const response2: ApiResponse<string[]> = {
data: ["specific", "data"],
status: 200,
message: "Success"
};

Best Practices

  1. Use descriptive type parameter names
    • T for general types
    • K for key types
    • V for value types
  2. Keep generic functions focused
  3. Use constraints to make your code more predictable
  4. Consider providing default types when appropriate

Practice Exercise

Create a generic Stack<T> class with push, pop, and peek methods that can work with any type. Include appropriate error handling for empty stacks.