[Medium] Generics
1. What are generics?
What problem do generics solve in TypeScript?
Generics let you write reusable, type-safe code without hard-coding one concrete type.
Without generics, you often duplicate functions for each type. With generics, one implementation can work for many types while preserving type information.
2. Generic functions
Basic syntax
function identity<T>(value: T): T {
return value;
}
const n = identity<number>(123);
const s = identity('hello'); // type is inferred as string
Generic array helper
function firstItem<T>(items: T[]): T | undefined {
return items[0];
}
const firstNumber = firstItem([1, 2, 3]);
const firstString = firstItem(['a', 'b']);
3. Generic constraints
Sometimes you need a generic type with required fields.
function getLength<T extends { length: number }>(value: T): number {
return value.length;
}
getLength('TypeScript');
getLength([1, 2, 3]);
// getLength(123); // Error: number has no length
extends here means "must satisfy this shape".
4. Multiple type parameters
function toPair<K, V>(key: K, value: V): [K, V] {
return [key, value];
}
const pair = toPair('id', 1001); // [string, number]
This is common in maps, dictionaries, and data transformation utilities.
5. Generic interfaces and type aliases
Generic interface
interface ApiResponse<T> {
success: boolean;
data: T;
error?: string;
}
const userResponse: ApiResponse<{ id: number; name: string }> = {
success: true,
data: { id: 1, name: 'Pitt' },
};
Generic type alias
type Result<T> =
| { ok: true; value: T }
| { ok: false; message: string };
6. Generic classes
class Queue<T> {
private items: T[] = [];
enqueue(item: T): void {
this.items.push(item);
}
dequeue(): T | undefined {
return this.items.shift();
}
}
const numberQueue = new Queue<number>();
numberQueue.enqueue(10);
7. Default generic types
You can define fallback types.
type ApiResult<T = string> = {
data: T;
status: number;
};
const a: ApiResult = { data: 'ok', status: 200 };
const b: ApiResult<number> = { data: 1, status: 200 };
8. keyof with generics
function pick<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { id: 1, name: 'Alex' };
const name = pick(user, 'name'); // string
This pattern is core to many utility types.
9. Common mistakes and best practices
Common mistakes
- Using too many generic parameters without clear purpose
- Naming everything
Tin complex APIs - Falling back to
anyinstead of proper constraints
Best practices
- Use descriptive names in complex cases (
TItem,TValue) - Add constraints where behavior depends on shape
- Prefer inference first, explicit type arguments only when needed
- Keep generic APIs small and focused
10. Quick interview answers
Q1: What is the biggest benefit of generics?
Reusable code with compile-time type safety.
Q2: What does T extends U mean?
T must be assignable to U; it is a generic constraint.
Q3: When should I avoid generics?
When the abstraction does not improve clarity or only supports one concrete type.