On this page
Generics
Generics let you write code that works with many types while preserving type information. Instead of using any, you declare a type parameter that callers specify.
Generic Functions
function identity<T>(value: T): T {
return value;
}
const num = identity<number>(42); // T is number
const str = identity('hello'); // T inferred as string
function firstItem<T>(items: T[]): T | undefined {
return items[0];
}
console.log(firstItem([1, 2, 3])); // number | undefined
console.log(firstItem(['a', 'b'])); // string | undefined
Generic Interfaces
interface ApiResponse<T> {
data: T;
status: number;
error: string | null;
}
interface User {
id: number;
name: string;
}
const response: ApiResponse<User> = {
data: { id: 1, name: 'Alice' },
status: 200,
error: null
};
Generic Classes
class Stack<T> {
private items: T[] = [];
push(item: T): void {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
peek(): T | undefined {
return this.items[this.items.length - 1];
}
}
const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
console.log(numberStack.pop()); // 2
const stringStack = new Stack<string>();
stringStack.push('hello');
// stringStack.push(42); // Error
Constraints
Limit what types a generic can accept with extends:
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(item: T): number {
console.log(item.length);
return item.length;
}
logLength('hello'); // 5
logLength([1, 2, 3]); // 3
// logLength(42); // Error: number has no .length
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const person = { name: 'Alice', age: 30 };
console.log(getProperty(person, 'name')); // "Alice"
// getProperty(person, 'email'); // Error
Multiple Type Parameters
function pair<A, B>(first: A, second: B): [A, B] {
return [first, second];
}
const result = pair('Alice', 25);
// result: [string, number]
Default Type Parameters
interface PaginatedResult<T = unknown> {
items: T[];
page: number;
total: number;
}
Generic Type Aliases
type Result<T, E = Error> =
| { success: true; value: T }
| { success: false; error: E };
function parseNumber(input: string): Result<number> {
const n = Number(input);
if (isNaN(n)) {
return { success: false, error: new Error('Invalid number') };
}
return { success: true, value: n };
}
Generics are everywhere in TypeScript libraries — arrays, promises, React hooks, and API clients all rely on them. Next, we explore built-in utility types that transform existing types.