On this page
The core idea
A generic function declares a type parameter, such as T, that stands for whatever type the caller provides. TypeScript infers T from the arguments and uses it in the return type, so a function like first<T>(items: T[]): T | undefined returns a Course when given courses and a string when given strings. Constraints such as T extends { id: string } restrict which types are allowed while still preserving the specific type. The result is reusable code that does not weaken the types around it.
A practical approach
Step 1
Look for helpers typed with any or unknown that return part of their input, such as sorting, grouping, picking or caching functions.
Step 2
Introduce a type parameter for the input and use it in the return type so information flows through.
Step 3
Add a constraint when the function needs certain properties, for example groupBy<T, K extends string>(items: T[], key: (item: T) => K).
Step 4
Let TypeScript infer type arguments at call sites instead of writing them explicitly.
Step 5
Stop when the generic signature becomes harder to read than two separate functions; clarity matters more than reuse.
Worked example
A shared byId(items: any[]) helper returned a lookup object of any values, so code using it could read misspelled properties without errors. Rewriting it as byId<T extends { id: string }>(items: T[]): Record<string, T> made every lookup return the correct type. Two misspelled property reads in the catalogue page appeared immediately as compiler errors.
A mistake to avoid
Avoid adding type parameters that appear only once in a signature. A parameter used only in the return type, for example, forces callers to guess and usually hides an unsafe cast inside the function.
Try it yourself
Find one helper in your codebase that uses any. Rewrite it with a generic and a constraint, then check that call sites still compile and that hovering over the result in your editor shows the specific type.
Your implementation checklist
- Helpers preserve input types in their output
- Constraints describe only the properties the function needs
- Type arguments are inferred at call sites
- Every type parameter relates at least two positions
- Generic signatures remain readable
Share or cite this guide
Innovate with Ajay Editorial (2026-10-07). Use TypeScript Generics to Keep Helper Functions Precise. https://innovatewithajay.com/blogs/use-typescript-generics-in-helpers
