TypeScriptJavaScriptBest Practices
Mastering TypeScript Generics: From Basics to Advanced Patterns
Jan 14, 2026·10 min read
Unlock the full power of TypeScript generics — conditional types, mapped types, infer keyword, and real-world utility type patterns that make codebases type-safe and maintainable.
Why Generics?
Generics let you write functions and data structures that work over a variety of types while preserving type information. Without them you'd reach for `any` and lose all the safety TypeScript gives you.
typescript
// Without generics — information is lost
function first(arr: any[]): any { return arr[0]; }
// With generics — the return type matches the array element type
function first<T>(arr: T[]): T | undefined { return arr[0]; }
const n = first([1, 2, 3]); // number | undefined ✓
const s = first(['a', 'b']); // string | undefined ✓Conditional Types
Conditional types let you express branching type logic: `T extends U ? X : Y`. They are the foundation for most advanced utility types in the TypeScript standard library.
typescript
type IsArray<T> = T extends any[] ? true : false;
type A = IsArray<string[]>; // true
type B = IsArray<number>; // false
// Unwrap the element type of any array
type Unwrap<T> = T extends (infer Item)[] ? Item : T;
type C = Unwrap<string[]>; // string
type D = Unwrap<number>; // numberMapped Types
Mapped types iterate over the keys of an existing type to produce a new one. All built-in utility types like `Partial<T>`, `Readonly<T>`, and `Required<T>` are just mapped types under the hood.
typescript
// Make every property optional and nullable
type Nullable<T> = { [K in keyof T]: T[K] | null };
interface User { id: number; name: string; email: string }
type NullableUser = Nullable<User>;
// { id: number | null; name: string | null; email: string | null }Real-World: A Type-Safe API Client
typescript
type ApiRoutes = {
'/users': { response: User[] };
'/users/:id': { params: { id: string }; response: User };
'/posts': { response: Post[] };
};
async function get<P extends keyof ApiRoutes>(
path: P,
): Promise<ApiRoutes[P]['response']> {
const res = await fetch(path);
return res.json();
}
const users = await get('/users'); // type: User[]
const post = await get('/posts'); // type: Post[]“TypeScript generics are not just about reusability — they are about making impossible states unrepresentable at compile time.”