Why Generics?
Generics let you write functions and data structures that work across multiple types while still maintaining full type safety. Without them, you'd have to choose between duplicating code for every type or losing type information entirely with any.
Your First Generic Function
Here's the classic example — an identity function that returns whatever you pass in:
function identity<T>(value: T): T {
return value
}
const num = identity(42) // type: number
const str = identity("hello") // type: string
The T is a type parameter — a placeholder that TypeScript fills in based on what you actually pass.
Generic Constraints
Sometimes you need to constrain what types are allowed. Use extends to enforce a minimum shape:
function getLength<T extends { length: number }>(value: T): number {
return value.length
}
getLength("hello") // ✓
getLength([1, 2, 3]) // ✓
getLength(42) // ✗ — number has no .length
Generic Interfaces and Types
Generics shine in data structure definitions. A reusable API response wrapper:
interface ApiResponse<T> {
data: T
status: number
message: string
}
type UserResponse = ApiResponse<{ id: string; name: string }>
type PostListResponse = ApiResponse<Post[]>
Multiple Type Parameters
You can have more than one. The built-in Record utility type is defined as Record<K, V> — a common pattern when you need to relate two types.
function zip<A, B>(a: A[], b: B[]): [A, B][] {
return a.map((item, i) => [item, b[i]])
}
zip([1, 2], ["a", "b"]) // [number, string][]Discussion
Loading comments…