TypeScript 5.4: More Powerful Types, Fewer Tricks
Actualizado: 2026-05-03
TypeScript 5.4 shipped on March 6, 2024, and fits the category of releases marketed as “incremental” whose real day-to-day impact is disproportionate. It does not bring a paradigm shift comparable to satisfies or template literal types, but it retires three or four frictions that had been present in every moderately generic TypeScript codebase for years.
Key takeaways
NoInfer<T>eliminates the two-type-parameter pattern used to constrain inference in generic functions.- Narrowing inside synchronous callbacks (
forEach,map) no longer requires defensive intermediate variables. Object.groupByandMap.groupByare now typed, closing the last excuse for pulling in lodash just to group lists.- Compiler performance improvements are visible in large monorepos, not in small projects.
- Migration from 5.3 is
npm install -D typescript@5.4for the overwhelming majority of projects.
NoInfer: The Fix We Had Been Asking For
The headline addition is the NoInfer<T> utility type. It solves a specific but ubiquitous problem: when a generic function takes multiple parameters mentioning the same type parameter, TypeScript infers it from all of them, widening the resulting type beyond the author’s intent.
Before 5.4 the fix was either two separate type parameters or an obscure conditional type; now it is a trivial annotation:
function createStreetLight<C extends string>(
colors: C[],
defaultColor?: NoInfer<C>
) {}
createStreetLight(["red", "yellow", "green"], "blue");
// Error: '"blue"' is not assignable to '"red" | "yellow" | "green"'
function withDefault<T>(value: T, fallback: NoInfer<T>): T {
return value ?? fallback;
}
withDefault(42, 0); // T = number, ok
withDefault("hi", 42); // Error — T pinned to stringThe authors of Zod, tRPC, and React Query are adopting it in their next major versions, so it reaches any modern project indirectly.
Narrowing Preserved in Synchronous Closures
The second change you will notice is narrowing preserved inside synchronously-executed callbacks. The compiler now recognises that standard array methods run synchronously and keeps the narrowing alive inside the callback. The practical result:
- Fewer
as string[]scattered around. - Fewer defensive
if (typeof x === 'string')blocks that added nothing. - Fewer intermediate variables created solely to capture the narrowed type.
Object.groupBy and Map.groupBy
TS 5.4 types Object.groupBy and Map.groupBy, which were at TC39 stage 3 at release time. The signature returns Partial<Record<K, T[]>>, with the Partial correctly marking that not every key in the union will appear. It removes a lodash dependency in projects that only used it for this.
Compiler Performance
The release notes mention lower memory use, faster incremental builds, and better type-instance deduplication. In a ten-file project you will notice nothing. In a monorepo with hundreds of packages and a tsc --build that took minutes, the difference is perceptible but not transformative.
What It Does Not Bring and What Can Break
Breaking changes are minor and localised: stricter homomorphism in mapped type variations may catch libraries doing aggressive type gymnastics, and some Function.prototype.bind signatures tighten. In application code the probability of noticing is essentially zero.
Upgrading from 5.3
For most projects, migration is:
npm install -D typescript@5.4- Run
tsc --noEmitand check for new errors. - If you use
@typescript-eslint, bump the parser to a compatible version in the same commit.
Vite, esbuild, swc, Next, Nuxt, SvelteKit, and the rest of the modern stack need no changes. It is one of the least traumatic releases in years.
Conclusion
TypeScript 5.4 does not change how you think about types, but it retires three workarounds you had been writing for years without quite noticing. NoInfer turns into a trivial annotation what used to require two type parameters or an obscure conditional type. Closure narrowing eliminates an entire category of annoying casts. Object.groupBy closes the last excuse for pulling in lodash just to group a list. For an active team, upgrading early pays off: the new patterns make code more expressive, and the old ones start smelling like legacy faster than expected.