Overview
Generics let you write functions, classes, and interfaces that work with multiple types while preserving type information. They are the difference between a function that returns any and one that returns exactly the type you put in.
Without Generics: The Problem
function identity(value: any): any {
return value;
}
const result = identity("hello"); // result is any
The caller loses all type information. Autocomplete stops working, and type errors go undetected.
With Generics: The Solution
function identity<T>(value: T): T {
return value;
}
const result = identity("hello"); // result is string
T is a type parameter, a placeholder that TypeScript fills in when the function is called.
Generic Constraints
Sometimes you need to guarantee that T has certain properties. Use the extends keyword:
function getLength<T extends { length: number }>(value: T): number {
return value.length;
}
getLength("hello"); // OK
getLength([1, 2, 3]); // OK
getLength(42); // Error: number does not have length
Generic Interfaces
interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
const userResponse: ApiResponse<{ name: string }> = {
data: { name: "Alice" },
status: 200,
message: "OK"
};
Generic Classes
class Stack<T> {
private items: T[] = [];
push(item: T): void {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
}
const numbers = new Stack<number>();
numbers.push(1);
numbers.push(2);
const top = numbers.pop(); // number | undefined
Default Type Parameters
function createPair<K, V = string>(key: K, value: V): [K, V] {
return [key, value];
}
createPair("id", "123"); // [string, string]
createPair("count", 42); // [string, number]
Common Generic Patterns
| Pattern | Syntax | Use case |
|---|---|---|
| Identity function | function f<T>(x: T): T | Preserve input type |
| Constraint | function f<T extends U>(x: T) | Require specific properties |
| Multiple parameters | function f<K, V>(k: K, v: V) | Independent types |
| Default type | <T = string> | Optional type argument |
| Keyof constraint | <T, K extends keyof T> | Type-safe property access |
Real-World Example: Typed API Client
async function get<T>(url: string): Promise<T> {
const response = await fetch(url);
if (!response.ok) throw new Error(response.statusText);
return response.json() as Promise<T>;
}
interface User {
id: number;
name: string;
}
const user = await get<User>("/api/users/1");
// user is typed as User 