TypeScript Generics and Function Types
Once you can name a shape, the next question is how to reuse it without copying it. This topic covers the three tools TypeScript gives you for that: function types that describe what a helper accepts and returns, generics that carry a caller's type through a helper untouched, and utility types that build new shapes out of shapes you already declared.
These three fit together more tightly than their names suggest. A utility type such as ReturnType is a generic type applied to a function type, so the vocabulary of the first guide shows up in the last one. Read them in order if any of that is new.
TypeScript Generics Topics
- Typing Functions and Callbacks covers parameters, return types, optional and rest parameters, callback types, and where contextual typing saves you the annotation.
- Generic Functions and Types covers type parameters, inference at the call site, constraints with
extends, and defaults. - Utility Types covers the built-in transformers such as
Pick,Omit,Partial,Record, andReturnType, plus when a named type beats a clever one.
The Rule Everything Here Depends On
A generic is a type parameter: a placeholder the caller fills in, exactly the way a function parameter is a value placeholder. Nothing more mystical is going on, and seeing the two side by side usually settles it:
// a value parameter: the caller supplies the value
function identityValue(value: string): string {
return value;
}
// a type parameter: the caller supplies (or the compiler infers) the type
function identity<Value>(value: Value): Value {
return value;
}
const label = identity("draft");
const count = identity(3);
// hover in an editor shows:
// const label: "draft"
// const count: 3 The compiler read each call and filled Value in for you, which is inference doing the same job it does for ordinary variables. Those two results are literal types because the arguments were literals passed straight into an unconstrained type parameter; the same calls assigned through a wider annotation would widen to string and number.
The second half of the rule is that all of this disappears. Type parameters, constraints, and utility types are part of the type layer, so compilation erases them and the emitted JavaScript holds one plain function. That is why a generic cannot inspect its own type argument at runtime: there is nothing left to inspect. Checking what a value actually is remains a narrowing problem, which the types topic covers.
Common Pitfalls
- A type parameter used once: if
Tappears in exactly one position and nowhere else, it is usually not connecting anything, though a parameter used once can still capture a type for a later inference. The generics guide covers the test for whether a parameter is pulling weight. - Annotating callback parameters that infer: a callback passed straight into a typed parameter is contextually typed already. The functions guide shows where that inference applies and where it stops.
- Reaching for
anyto make a helper reusable: that makes the helper accept everything and return nothing useful. A type parameter keeps the caller's type instead of discarding it. - Nesting utility types until nobody can read them:
Partial<Pick<User, "email">>is fine once. Repeated in three files, it wants a name, as the utility types guide argues.
Common Questions
What does T actually mean?
T is just a parameter name for a type, the way x is a parameter name for a value. The single letter is a convention inherited from other languages, not a rule: GenericItem or ItemType compiles identically. Longer names usually read better once a helper takes more than one type parameter.
Do you have to write the type argument yourself?
Usually not. When a type parameter appears in a position the arguments contribute to, the compiler infers it from the call site, though some positions supply no candidate and NoInfer blocks one deliberately. You supply it yourself when the call constrains nothing, or when you want a wider type than inference picked.
Are generics slower at runtime?
No. Type parameters are part of the type layer, so they are erased during compilation and the shipped JavaScript contains a single ordinary function. The cost lands on the compiler and the editor, which is why deeply nested generic types can slow type checking while leaving runtime performance untouched.
Continue Learning TypeScript
- Start with TypeScript Types if annotations, interfaces, and unions are still new. Everything here builds on them.
- Move on to TypeScript Tooling for
tsconfig.json, the compiler flags that change how strict all of this is, React props, and API contracts. - Return to the TypeScript guide for the language overview and the full learning path.
Sources
-
[1]
More on Functions(typescriptlang.org)
-
[2]
Generics(typescriptlang.org)
-
[3]
Utility Types(typescriptlang.org)
Read Next
Parameter and return annotations, optional and rest parameters, named function types, and the contextual typing that removes most callback annotations.
Type parameters explained without the theory: what a generic buys you, how inference fills it in at the call site, constraints with extends, generic types, and defaults.
Learn TypeScript with practical coverage of types, interfaces, unions, narrowing, generics, utility types, tsconfig, React patterns, API contracts, and AI-assisted review.