What are Generics?
Generics allow you to write flexible, reusable code that works with multiple types while maintaining type safety. Think of them as "type variables."
Basic Generic Function
Without generics, you'd need to write separate functions for each type:
typescript
Copy
1// Without generics - repetitive 2function identityString(arg: string): string { 3 return arg; 4} 5 6function identityNumber(arg: number): number { 7 return arg; 8} 910// With generics - reusable11function identity<T>(arg: T): T {12 return arg;13}1415// TypeScript infers the type16const str = identity("hello"); // type: string17const num = identity(42); // type: number
Generic Interfaces
Create flexible interfaces that work with any type:
typescript
Copy
1interface ApiResponse<T> { 2 data: T; 3 status: number; 4 message: string; 5} 6 7interface User { 8 id: number; 9 name: string;10 email: string;11}1213// Usage14const userResponse: ApiResponse<User> = {15 data: { id: 1, name: "John", email: "john@example.com" },16 status: 200,17 message: "Success"18};
Generic Constraints
Limit what types can be used with extends:
typescript
Copy
1interface Lengthwise { 2 length: number; 3} 4 5function logLength<T extends Lengthwise>(arg: T): T { 6 console.log(arg.length); 7 return arg; 8} 910logLength("hello"); // ✅ Strings have length11logLength([1, 2, 3]); // ✅ Arrays have length12logLength({ length: 5 }); // ✅ Object with length13logLength(123); // ❌ Error: number has no length
Generic Classes
Create type-safe data structures:
typescript
Copy
1class Stack<T> { 2 private items: T[] = []; 3 4 push(item: T): void { 5 this.items.push(item); 6 } 7 8 pop(): T | undefined { 9 return this.items.pop();10 }11 12 peek(): T | undefined {13 return this.items[this.items.length - 1];14 }15}1617const numberStack = new Stack<number>();18numberStack.push(1);19numberStack.push(2);20console.log(numberStack.pop()); // 22122const stringStack = new Stack<string>();23stringStack.push("hello");24stringStack.push("world");
Multiple Type Parameters
Use multiple generics for complex scenarios:
typescript
Copy
1function pair<K, V>(key: K, value: V): [K, V] { 2 return [key, value]; 3} 4 5const result = pair("name", "John"); // type: [string, string] 6const entry = pair(1, true); // type: [number, boolean] 7 8// Real-world example: Map function 9function mapArray<T, U>(arr: T[], fn: (item: T) => U): U[] {10 return arr.map(fn);11}1213const numbers = [1, 2, 3];14const strings = mapArray(numbers, n => n.toString());15// strings type: string[]
Utility Types with Generics
TypeScript provides powerful built-in generic types:
typescript
Copy
1interface User { 2 id: number; 3 name: string; 4 email: string; 5 password: string; 6} 7 8// Partial<T> - All properties optional 9type PartialUser = Partial<User>;1011// Pick<T, K> - Select specific properties12type UserPreview = Pick<User, 'id' | 'name'>;1314// Omit<T, K> - Exclude properties15type SafeUser = Omit<User, 'password'>;1617// Record<K, V> - Object with specific key/value types18type UserMap = Record<string, User>;
Conclusion
Generics are essential for writing professional TypeScript code. They enable you to create flexible, reusable components while maintaining full type safety. Master these patterns and you'll write better TypeScript.