Generics cheatsheet.
Generic functions
function identity<T>(x: T): T {
return x;
}
identity(42); // T inferred as number
identity<string>("hi"); // explicit
Multiple type params
function pair<A, B>(a: A, b: B): [A, B] {
return [a, b];
}
pair("hi", 42); // [string, number]
Constraints (extends)
function len<T extends { length: number }>(x: T): number {
return x.length;
}
len("hi"); // ok
len([1, 2, 3]); // ok
len(42); // ERROR: no length
keyof
function pluck<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const u = { id: 1, name: "A" };
pluck(u, "id"); // number
pluck(u, "missing"); // ERROR
Defaults
function box<T = string>(x: T): { value: T } {
return { value: x };
}
box("hi"); // T = string
box<number>(42);
Generic interfaces / type aliases
interface Result<T, E = Error> {
ok: boolean;
value?: T;
error?: E;
}
type Pair<A, B = A> = [A, B];
Generic classes
class Stack<T> {
private items: T[] = [];
push(x: T) { this.items.push(x); }
pop(): T | undefined { return this.items.pop(); }
}
const s = new Stack<number>();
Conditional types
type IsArray<T> = T extends any[] ? true : false;
type A = IsArray<number[]>; // true
type B = IsArray<string>; // false
infer
type ReturnOf<T> = T extends (...args: any[]) => infer R ? R : never;
type R = ReturnOf<() => string>; // string
// Unwrap promise
type Awaited2<T> = T extends Promise<infer U> ? U : T;
Distribution over unions
type ToArray<T> = T extends any ? T[] : never;
type X = ToArray<string | number>; // string[] | number[]
Wrap to prevent distribution:
type ToArray<T> = [T] extends [any] ? T[] : never;
type X = ToArray<string | number>; // (string | number)[]
Mapped types
type Optional<T> = { [K in keyof T]?: T[K] };
type Readonly2<T> = { readonly [K in keyof T]: T[K] };
type Stringify<T> = { [K in keyof T]: string };
Mapped with as (remap keys)
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type G = Getters<{ id: number; name: string }>;
// { getId: () => number; getName: () => string }
Generic constraint with default
function getById<T extends { id: number } = { id: number }>(
items: T[],
id: number
): T | undefined {
return items.find(x => x.id === id);
}
Generic factories
function create<T>(Ctor: new () => T): T {
return new Ctor();
}
class Widget {}
const w = create(Widget); // Widget
Generic discriminated unions
type Result<T> =
| { ok: true; value: T }
| { ok: false; error: string };
function unwrap<T>(r: Result<T>): T {
if (r.ok) return r.value;
throw new Error(r.error);
}
NoInfer (5.4+)
function call<T>(value: T, transformer: (x: NoInfer<T>) => T): T {
return transformer(value);
}
call("hi", (x: string) => x.toUpperCase());
// T inferred from first arg, NOT the second
Without NoInfer, both args contribute to inference — sometimes you want only one.
Higher-kinded patterns (workaround)
TS lacks true HKT, but you can simulate with interfaces:
interface Functor<F> {
map<A, B>(fa: F & A, f: (a: A) => B): F & B;
}
Use sparingly — usually a sign you’re overcomplicating.
Generic React components
function List<T>({ items, render }: {
items: T[];
render: (item: T) => React.ReactNode;
}) {
return <ul>{items.map((it, i) => <li key={i}>{render(it)}</li>)}</ul>;
}
<List items={[1, 2, 3]} render={(n) => n * 2} />
In .tsx the <T> is ambiguous with JSX. Workaround: <T,> or <T extends unknown>.
Variance (4.7+)
type Animal = { name: string };
type Dog = Animal & { bark(): void };
type Producer<out T> = () => T; // covariant
type Consumer<in T> = (x: T) => void; // contravariant
type Bivariant<in out T> = ...;
Common mistakes
<T>without using T — just remove it.- Constraints too loose —
T extends objectaccepts arrays, functions, etc. - Distribution surprise —
T extends X ? ... : ...over a union. - Returning
TfromT extends Ybody — TS doesn’t narrow T inside the conditional. - Generic on every function — sometimes a union or overload is clearer.
Read this next
If you want my generic utility library, it’s at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .