TypeScript Lesson 7: Generics
Generics let you write reusable code that works with any type while keeping type safety. They’re like type parameters — the most powerful TypeScript feature.
Generic Functions
// Without generics: only works with strings
function firstString(arr: string[]): string { return arr[0]; }
// With generics: works with ANY type
function first<T>(arr: T[]): T {
return arr[0];
}
const name = first(["Alice", "Bob"]); // T = string
const num = first([1, 2, 3]); // T = number
const user = first(users); // T = User
// Multiple type parameters
function zip<A, B>(a: A[], b: B[]): [A, B][] {
return a.map((item, i) => [item, b[i]]);
}
zip([1, 2, 3], ["a", "b", "c"]); // [[1,"a"],[2,"b"],[3,"c"]]
Generic Interfaces
interface ApiResponse<T> {
data: T;
status: number;
message: string;
timestamp: Date;
}
interface PaginatedResponse<T> {
items: T[];
total: number;
page: number;
perPage: number;
hasMore: boolean;
}
// Usage
async function getUsers(): Promise<ApiResponse<User[]>> { ... }
async function getPosts(page: number): Promise<PaginatedResponse<Post>> { ... }
Constrained Generics
// T must have a length property
function longest<T extends { length: number }>(a: T, b: T): T {
return a.length >= b.length ? a : b;
}
longest("hello", "hi"); // OK — string has .length
longest([1, 2, 3], [1, 2]); // OK — array has .length
longest(42, 100); // ❌ Error — number has no .length
// keyof constraint
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
getProperty(user, "name"); // OK, returns string
getProperty(user, "xyz"); // ❌ Error — not a key of User
🏋️ Practice Task
Build a generic Stack
💡 Hint: class Stack