TypeScript Generics: Practical Patterns You Will Actually Use
Generics are where TypeScript goes from "JavaScript with type annotations" to a genuinely powerful type system. They are also where most developers' eyes glaze over. The official documentation is thorough but dense, and most examples use T, U, V variable names that tell you nothing about what the type represents.
I am going to explain generics through real patterns that I use constantly in production code. No contrived examples — every pattern here is something I have written in actual applications.
The Mental Model
A generic is a type parameter — a placeholder for a type that will be filled in later. Just like a function parameter is a placeholder for a value:
Function: function add(a: number, b: number): number — a and b are value placeholders filled in at the call site.
Generic: function first
The key insight: generics let you write one function/type/class that works correctly with multiple types while maintaining type safety. Without generics, you would write firstNumber, firstString, firstUser — or use any and lose safety.
Pattern 1: Generic API Response Wrapper
Every API route returns the same shape: either data or an error. Instead of defining the response type per endpoint:
Define a generic ApiResponse
This pattern extends naturally to pagination: PaginatedResponse
Pattern 2: Generic Data Fetching Hook
In React, you write hooks that fetch data from APIs. Without generics, you write useUser, useTools, useBlogPosts — each doing the same fetch-loading-error dance with different types.
With generics: useQuery
The real-world version adds caching, revalidation, and error retry — but the generic structure remains the same. The type parameter flows through all the internal logic, ensuring the cached value, the loading state, and the error all have correct types.
Pattern 3: Generic Form Handlers
Forms are the most common place where generics pay off immediately. A form handler that takes an initial state, validates it, and calls a submit function:
function useForm
The type T flows through the entire form: the initial values are typed, the validation function receives correctly typed values, the submit handler receives the final typed values, and the individual field accessor knows the type of each field.
Without generics, you either use any (unsafe) or define a separate form type for every form in your application (tedious). With generics, one useForm hook handles every form with full type safety.
Pattern 4: Generic Database Repository
When working with Prisma or any ORM, you often write repository functions that do similar operations on different models. A generic base repository eliminates repetition:
Define a Repository
Each specific repository (UserRepository, ToolRepository) extends the generic base and adds model-specific methods. The common CRUD operations are written once with full type safety.
The constraint is important: T extends { id: string; createdAt: Date } ensures the generic only works with models that have the expected fields. Without the constraint, you cannot safely access id or createdAt inside the generic methods.
Pattern 5: Generic Event System
Event-driven architectures benefit enormously from generics. Define an event map that associates event names with their payload types:
type EventMap = { 'user:created': { userId: string; email: string }, 'tool:viewed': { toolId: string; userId?: string }, 'review:submitted': { toolId: string; rating: number; userId: string } }
Then define an emit function: function emit
Similarly, the subscribe function: function subscribe
Pattern 6: Generic Factory Functions
Factory functions that create things with different configurations are perfect for generics:
function createTool
This pattern is powerful with discriminated unions. The generic constraint narrows based on the discriminant field, and the return type adjusts accordingly.
Pattern 7: Conditional Types for API Responses
Advanced but incredibly useful: make the return type depend on input parameters.
function fetchData
This replaces the common pattern of switch statements with manual type assertions. The type system does the narrowing for you.
Pattern 8: Generic Component Props
React components with render props or callback props benefit from generics:
A generic List
A generic Select
Common Mistakes
Using any instead of generics. If you catch yourself writing function process(data: any): any, you probably need a generic. Generics maintain type safety where any destroys it.
Over-constraining generics. Adding too many extends constraints makes generics inflexible. Start with minimal constraints and add them only when the implementation requires accessing specific properties.
Naming generics T, U, V without context. Name them meaningfully: TData, TResponse, TItem. Future readers (including you in three months) will thank you.
Generic proliferation. Not everything needs to be generic. If a function only ever handles one type, use that type directly. Generics add complexity — use them when you genuinely need to handle multiple types.
The Learning Path
Start with generic functions (easiest to understand — one type parameter that flows through input to output). Move to generic types and interfaces (define flexible data structures). Then tackle generic constraints (narrow what types are allowed). Finally, explore conditional types and mapped types (transform types programmatically).
Each step builds on the previous one. Do not jump to conditional types before you are comfortable with basic generic functions. The jump in complexity is significant and will confuse you if the foundations are not solid.
Generics are the feature that separates TypeScript from "typed JavaScript." Master them and you will write code that is simultaneously more flexible and more type-safe — which is the entire promise of TypeScript.
Written by AIDToolStack Editorial
The AIDToolStack editorial team reviews developer tools, AI products, and MCP servers with hands-on testing and honest assessments. We use the tools we review every day in production.
References & Further Reading
Related Posts
Sponsor Our Newsletter
Reach thousands of developers who are actively evaluating AI tools, MCP servers, and dev infrastructure. Our weekly newsletter goes to engaged technical decision-makers.
All sponsored content is clearly labeled per our editorial policy.