Syntax
function name<T extends Constraint>(param: T) { }Examples
Constraining to Objects with a Property
Ensuring a generic type has at least a specific property before accessing it.
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(item: T): T {
console.log(`Length: ${item.length}`);
return item;
}
logLength("hello"); // OK - strings have .length
logLength([1, 2, 3]); // OK - arrays have .length
// logLength(42); // Error - number has no .length propertykeyof Constraint for Safe Property Access
A common pattern: constraining a key parameter to only the actual keys of an object.
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { name: "Alice", age: 25 };
const name = getProperty(user, "name"); // OK, correctly typed as string
// getProperty(user, "email"); // Error - "email" is not a key of userBest practices
- Use extends to constrain a generic parameter whenever the function body needs to access specific properties or methods on it
- Use the K extends keyof T pattern for functions that access an object property by a dynamic key - it keeps both the object and the resulting value type-safe
- Keep constraints as loose as possible while still enabling the functionality you need, to keep the generic function usable with the widest range of types
- Combine multiple constraints with an intersection type (T extends A & B) when a generic parameter needs to satisfy more than one shape
At a glance
- Purpose
- Static types for JavaScript
- File extension
- .ts ยท .tsx
- Runs in
- Compiled to JavaScript
- Usually used with
- JavaScript and its ecosystem
Specifications & further reading
Related TypeScript documentation
Generics Basics
Generics let you write reusable functions, classes, and types that work with a variety of types while still preserving type safety - rather than using any and losing that safety, or writing near-duplicate code for each type. A generic type parameter, conventionally named T, acts as a placeholder that gets filled in with a real type each time it is used.Generic Classes and Interfaces
Classes and interfaces can also be generic, letting you build reusable data structures (like a Stack, Queue, or API response wrapper) that work with any type while remaining fully type-safe for whichever specific type is used in each instance.Conditional Types
A conditional type selects between two types based on a condition, using syntax that mirrors JavaScript's ternary operator: T extends U ? X : Y. This lets you build types that adapt based on the shape of another type, forming the foundation for many of TypeScript's built-in utility types.The infer Keyword
infer, used only within the extends clause of a conditional type, lets you declare a new type variable that captures part of a matched structure, so it can be reused in the result. It is the mechanism behind utility types like ReturnType<T> and Parameters<T>, which extract specific pieces of a function's type signature.
Generics let you write reusable functions, classes, and types that work with a variety of types while still preserving type safety - rather than using any and losing that safety, or writing near-duplicate code for each type. A generic type parameter, conventionally named T, acts as a placeholder that gets filled in with a real type each time it is used.Generic Classes and Interfaces
Classes and interfaces can also be generic, letting you build reusable data structures (like a Stack, Queue, or API response wrapper) that work with any type while remaining fully type-safe for whichever specific type is used in each instance.Conditional Types
A conditional type selects between two types based on a condition, using syntax that mirrors JavaScript's ternary operator: T extends U ? X : Y. This lets you build types that adapt based on the shape of another type, forming the foundation for many of TypeScript's built-in utility types.The infer Keyword
infer, used only within the extends clause of a conditional type, lets you declare a new type variable that captures part of a matched structure, so it can be reused in the result. It is the mechanism behind utility types like ReturnType<T> and Parameters<T>, which extract specific pieces of a function's type signature.