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
- Use descriptive type parameter names
Tfor general typesKfor key typesVfor value types
- Keep generic functions focused
- Use constraints to make your code more predictable
- 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.