Generic programming means writing a function, class, or data structure that works across many types while the compiler still checks that the types make sense. Instead of writing separate versions of the same sorting function for numbers, strings, and custom objects, you write one generic version and let the specific type slot in at the call site. Using a loose catch-all type gives up that safety; copying and pasting one function per type gives up the reuse. Generics are the middle path.
What changed in 2026
- TypeScript inference for generics improved. Contextual typing and its interaction with the
satisfies operator reduce how often a type parameter needs to be spelled out manually for common cases.
- Go generics matured. Added to the language in 2022, generics are now used throughout more of the standard library and popular packages, changing what idiomatic Go looks like day to day.
- AI-assisted refactoring suggests generics often. Assistants frequently propose converting duplicated per-type functions into a single generic one, which is usually a real improvement, but the resulting constraint is worth checking rather than accepting a version typed with a loose catch-all just to make it compile.
A generic function, concretely
function firstElement<T>(items: T[]): T | undefined {
return items[0];
}
firstElement([1, 2, 3]); // T is inferred as number
firstElement(["a", "b"]); // T is inferred as string
T is a placeholder, filled in per call. The function works for every type, and the return type still matches whatever went in, unlike a version typed loosely, which would return an untyped value and silently lose that information.
Generics vs a loose catch-all type
|
Generic <T> |
Loose catch-all (any) |
| Type safety |
Preserved end to end |
Lost immediately |
| Autocomplete on the result |
Yes, matches input type |
No |
| Catches misuse at compile time |
Yes |
No |
| Good for |
Reusable, type-safe utilities |
Genuinely unknown, unstructured data |
Constraints: narrowing what a generic accepts
function longest<T extends { length: number }>(a: T, b: T): T {
return a.length >= b.length ? a : b;
}
longest("hello", "hi"); // works: strings have length
longest([1, 2, 3], [1]); // works: arrays have length
The extends clause says T can be anything, as long as it has a length property. This is generics and duck typing working together: the constraint describes a shape, not a specific class. See what duck typing is for the same idea without a compiler checking it.
Generics across languages
TypeScript, Java, and C# all use angle-bracket syntax for type parameters, though each erases or reifies the type information differently at run time. Go added generics with square-bracket type parameters in version 1.18. Rust generics combine with trait bounds and are monomorphized at compile time, producing specialized code with no runtime cost. Syntax and capability vary enough between languages that it is worth checking current documentation for the one you are using rather than assuming they all behave identically.
When to reach for a generic, and when not to
The same logic needs to work identically across several types: use a generic. The types involved are genuinely unrelated in shape: a union type or overloads may be clearer. Tempted to use a loose catch-all type just to make the compiler stop complaining: that is almost always a sign a generic, or a narrower type, is the better fix. A one-off internal function used in exactly one place with one type: just write the concrete type; the generic adds nothing there.
FAQ
Are generics the same as templates in C++?
Conceptually similar. Both let you write code parameterized by type, but the mechanics differ: C++ templates are compiled per instantiation, TypeScript generics are erased at compile time, and Rust monomorphizes similarly to C++.
Do generics slow down my code at run time?
In TypeScript and Java, no. The extra type information is either erased or checked at compile time, with no runtime cost tied to using a generic instead of a concrete type.
What is a good first generic to write?
A small generic wrapper or a single-purpose utility like a generic first-element function. Small, focused examples build intuition faster than a generic class with several type parameters.
How is a generic different from duck typing?
Generics are checked at compile time and require a statically typed language. Duck typing is typically a run-time concept. A constrained generic is close to compile-time duck typing.
Where to go next