TypeScript Generics
TypeScript generics let you write functions and types that work with many types while keeping full type safety. A generic uses a type parameter, usually called T, that is filled in when the code is used.
Why Generics?
Without generics you must choose between a specific type (not reusable) and any (no safety). A generic function remembers the type you pass in.
function first<T>(items: T[]): T | undefined {
return items[0];
}
const n = first([10, 20, 30]); // n: number | undefined
const s = first(["red", "blue"]); // s: string | undefined
console.log(n, s?.toUpperCase());10 RED
Multiple Type Parameters
TypeScript infers the type arguments from the values you pass, so you rarely write them explicitly.
function pair<K, V>(key: K, value: V): [K, V] {
return [key, value];
}
function mapValues<T, U>(arr: T[], fn: (item: T) => U): U[] {
return arr.map(fn);
}
console.log(pair("age", 30));
console.log(mapValues(["a", "bb", "ccc"], (s) => s.length));[ 'age', 30 ] [ 1, 2, 3 ]
Generic Constraints
Use extends to require that T has certain properties. keyof T restricts a parameter to the keys of T.
function longest<T extends { length: number }>(a: T, b: T): T {
return a.length >= b.length ? a : b;
}
function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
console.log(longest("apple", "kiwi"));
console.log(longest([1, 2], [1, 2, 3]).length);
const product = { name: "Tea", price: 6 };
console.log(getProp(product, "price"));
// getProp(product, "color"); // Error: '"color"' is not assignable to '"name" | "price"'.apple 3 6
Generic Interfaces and Types
Generic types are perfect for wrappers like API responses, where the envelope is the same but the data changes. Type parameters can have defaults.
interface ApiResponse<T, E = string> {
ok: boolean;
data?: T;
error?: E;
}
type User = { id: number; name: string };
const res: ApiResponse<User[]> = { ok: true, data: [{ id: 1, name: "Ali" }] };
console.log(res.data?.[0].name);Ali
Generic Classes
class Stack<T> {
#items: T[] = [];
push(item: T) { this.#items.push(item); }
pop(): T | undefined { return this.#items.pop(); }
get size() { return this.#items.length; }
}
const history = new Stack<string>();
history.push("/home");
history.push("/tutorials");
// history.push(42); // Error: 'number' is not assignable to 'string'.
console.log(history.pop(), history.size);/tutorials 1
Frequently asked questions
What does <T> mean in TypeScript?
It declares a type parameter named T, a placeholder for a type that is supplied (or inferred) when the function or type is used.
What is a generic constraint?
A rule written with extends, like T extends { id: number }, that limits which types can be used for T.
When should I use generics instead of any?
Whenever the output type depends on the input type. Generics preserve the type information; any throws it away.