Generic Functions
Generics let you write reusable code that works with multiple types while maintaining type safety. Think of them as type variables.
Example
// Without generics — loses type info
function identity(value: any): any {
return value;
}
// With generics — preserves type
function identity<T>(value: T): T {
return value;
}
let str = identity<string>("hello"); // type: string
let num = identity(42); // type: number (inferred)
// Generic with constraints
function getLength<T extends { length: number }>(item: T): number {
return item.length;
}
getLength("hello"); // 5
getLength([1, 2, 3]); // 3
getLength(123); // Error: number has no length Generic Interfaces & Types
You can use generics with interfaces and type aliases to create reusable data structures.
Example
// Generic interface
interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
type UserResponse = ApiResponse<{ name: string; email: string }>;
type ProductResponse = ApiResponse<{ id: number; price: number }>;
// Generic type with multiple parameters
type Pair<K, V> = {
key: K;
value: V;
};
const entry: Pair<string, number> = { key: "age", value: 25 }; Generic Constraints
Use extends to constrain what types a generic can accept.
Example
// Constrain to objects with an id
function findById<T extends { id: number }>(items: T[], id: number): T | undefined {
return items.find(item => item.id === id);
}
const users = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" }
];
const found = findById(users, 1); // type preserved
// keyof constraint
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { name: "Alice", age: 25 };
getProperty(user, "name"); // string
getProperty(user, "foo"); // Error: not a key of user 