Mastering Generics in TypeScript
Generics provide a way to create reusable components that work with any type while maintaining type safety.
Generic Functions
function identity(arg: T): T {
return arg;
}// Type inference
const str = identity("hello"); // string
const num = identity(42); // number
// Explicit type
const explicit = identity("hello");
Generic Constraints
interface Lengthwise {
length: number;
}function loggingIdentity(arg: T): T {
console.log(arg.length); // OK, we know it has length
return arg;
}
loggingIdentity("hello"); // OK
loggingIdentity([1, 2, 3]); // OK
loggingIdentity(42); // Error: number doesn't have length
Generic Classes
class DataStore {
private items: T[] = []; add(item: T): void {
this.items.push(item);
}
get(index: number): T | undefined {
return this.items[index];
}
getAll(): T[] {
return [...this.items];
}
}
const stringStore = new DataStore();
stringStore.add("hello");
const numberStore = new DataStore();
numberStore.add(42);
Multiple Type Parameters
function pair(key: K, value: V): [K, V] {
return [key, value];
}const p1 = pair("age", 30); // [string, number]
const p2 = pair(1, "one"); // [number, string]
// With constraints
function getProperty(obj: T, key: K): T[K] {
return obj[key];
}
const person = { name: "John", age: 30 };
const name = getProperty(person, "name"); // string
const age = getProperty(person, "age"); // number
Generic Defaults
interface APIResponse {
data: T;
status: number;
message: string;
}// Uses default type
const response: APIResponse = { data: null, status: 200, message: "OK" };
// Specifies type
const userResponse: APIResponse = {
data: { id: 1, name: "John", email: "john@example.com" },
status: 200,
message: "OK"
};