Jacar mascot — reading along A laptop whose eyes follow your cursor while you read.
Desarrollo de Software

TypeScript 5.4: More Powerful Types, Fewer Tricks

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.groupBy and Map.groupBy are 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.4 for 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:

typescript
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 string

The 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:

  1. npm install -D typescript@5.4
  2. Run tsc --noEmit and check for new errors.
  3. 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.

Was this useful?
[Total: 10 · Average: 4.8]

Written by

CEO - Jacar Systems

Passionate about technology, cloud infrastructure and artificial intelligence. Writes about DevOps, AI, platforms and software from Madrid.