// TypeScript infers T from the argument — no need to specify it manually
Note Type parameters are inferred from arguments in most cases. Only specify them explicitly when inference gives the wrong type. In .tsx files, arrow generics need a trailing comma: <T,>(param: T) to avoid JSX ambiguity.
generic functiontype parameterparameterized functionreusable function type
// Repository<User> locks T to User for the entire implementation
Note Generic interfaces are ideal for defining contracts that work across multiple entity types — repositories, services, collections. Each implementation locks in the concrete type.
class Name<T> {
constructor(private value: T) {}
}
example
class Stack<T> {
private items: T[] = [];
push(item: T): void {
this.items.push(item);
}
pop(): T | undefined {
returnthis.items.pop();
}
peek(): T | undefined {
returnthis.items[this.items.length - 1];
}
get size(): number {
returnthis.items.length;
}
}
const numberStack = new Stack<number>();
numberStack.push(10);
numberStack.push(20);
const top = numberStack.pop(); // number | undefined
output
// top → 20
Note Static members of a generic class cannot reference the class type parameter — the parameter only exists on instances. If you need generic static methods, make the method itself generic rather than the class.
generic classtyped classparameterized classdata structure class
Generic Constraints
syntax
function fn<T extends ConstraintType>(param: T): T { ... }
example
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(item: T): T {
console.log(`Length: ${item.length}`);
return item;
}
logLength("hello"); // string has .length
logLength([1, 2, 3]); // arrays have .length// logLength(42); // Error: number has no .length// Constrain to object keysfunction getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const color = getProperty({ r: 255, g: 128, b: 0 }, "g"); // number
output
// Constraints restrict what types a generic can accept
Note Use extends to set a minimum requirement for a type parameter. The constraint keyof T is extremely useful — it restricts a parameter to valid property names of another type, enabling fully type-safe property access.
// Default types make generics more ergonomic for common cases
Note Default type parameters work like default function arguments — they must come after required type parameters. A default does not constrain the type; add extends if you also need a constraint: <T extends object = Record<string, unknown>>.
default type parametergeneric defaultoptional type parameterdefault generic
Building Utilities with Generics
syntax
type UtilityName<T> = { [K in keyof T]: TransformedType };
example
// Make all properties optional and nullabletype Draft<T> = {
[K in keyof T]?: T[K] | null;
};
interface Article {
title: string;
body: string;
publishedAt: Date;
}
type ArticleDraft = Draft<Article>;
// { title?: string | null; body?: string | null; publishedAt?: Date | null }const draft: ArticleDraft = {
title: "Work in progress",
};
output
// Custom utility types combine generics with mapped types
Note This pattern (mapped types + generics) is how TypeScript's built-in utility types (Partial, Required, Readonly) are implemented. Understanding this lets you build project-specific utilities tailored to your domain.
custom utility typemapped type genericbuild utilitytransform type
Generics with Conditional Types
syntax
type Name<T> = T extends Condition ? TrueType : FalseType;
example
type IsArray<T> = T extends any[] ? true : false;
type Test1 = IsArray<string[]>; // truetype Test2 = IsArray<number>; // false// Extracting element typestype ElementOf<T> = T extends (infer E)[] ? E : never;
type StrEl = ElementOf<string[]>; // stringtype NumEl = ElementOf<number[]>; // numbertype NoEl = ElementOf<boolean>; // never
output
// Conditional types act like type-level if/else
Note When a conditional type receives a union as T, it distributes — the condition is applied to each union member independently. To prevent distribution, wrap both sides in brackets: [T] extends [Condition].
conditional generictype conditionalgeneric if elseinfer keyworddistribute over union