TypeScript 5.4: tipos más potentes, menos trucos
Actualizado: 2026-05-03
TypeScript 5.4 se publicó el 6 de marzo de 2024 y encaja en esa categoría de releases que se describen como “incrementales” pero cuyo impacto real en el día a día es desproporcionado. No introduce un cambio de paradigma comparable a satisfies o a los template literal types, pero retira tres o cuatro fricciones que llevaban años presentes en cualquier base de código TypeScript medianamente genérica.
Puntos clave
NoInfer<T>elimina el patrón de dos type parameters que se usaba para acotar inferencia en funciones genéricas.- El narrowing dentro de callbacks síncronos (
forEach,map) ya no requiere variables intermedias defensivas. Object.groupByyMap.groupByquedan tipados, cerrando la última excusa para importar lodash solo por agrupar listas.- Las mejoras de rendimiento del compilador se notan en monorepos grandes, no en proyectos pequeños.
- La migración desde 5.3 es
npm install -D typescript@5.4en la abrumadora mayoría de proyectos.
NoInfer: el arreglo que llevábamos años pidiendo
La estrella de la release es el utility type NoInfer<T>. Resuelve un problema específico pero omnipresente: cuando una función genérica toma varios parámetros que mencionan el mismo type parameter, TypeScript intenta inferirlo desde todos ellos, ensanchando el tipo resultante más allá de lo que el autor pretendía.
El caso canónico es una función que recibe una lista de valores permitidos y un valor por defecto. Si el segundo parámetro participa en la inferencia, puedes pasar un literal que no pertenece al conjunto y TypeScript lo aceptará, porque el propio acto de pasarlo ensancha C hasta incluirlo. Antes de 5.4 la solución pasaba por dos type parameters separados o un conditional type oscuro; ahora es una anotación trivial:
function createStreetLight<C extends string>(
colors: C[],
defaultColor?: NoInfer<C>
) {}
createStreetLight(["red", "yellow", "green"], "blue");
// Error: '"blue"' no es asignable a '"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 fijado a stringLos autores de Zod, tRPC y React Query lo incorporan en sus próximas versiones mayores, así que llegará a cualquier proyecto moderno aunque no lo uses explícitamente.
Narrowing preservado en closures síncronas
El segundo cambio que notarás esta semana es el narrowing preservado dentro de callbacks ejecutados síncronamente. Históricamente, un arr.every(x => typeof x === 'string') estrechaba el tipo de arr fuera del if, pero en cuanto entrabas en un forEach o map TypeScript olvidaba ese narrowing porque el callback podría, en teoría, ser llamado más tarde.
El compilador ahora reconoce que los métodos estándar de arrays son síncronos y mantiene el estrechamiento dentro del callback. El resultado práctico es:
- Menos
as string[]salpicados por el código. - Menos
if (typeof x === 'string')defensivos redundantes. - Menos variables intermedias creadas solo para capturar el tipo estrechado.
Encaja bien con bases de código que ya usan patrones funcionales sobre arrays en TypeScript y con proyectos que aprovechan Deno Deploy para edge computing, donde TypeScript nativo es el runtime.
Object.groupBy y Map.groupBy
TS 5.4 tipa los métodos Object.groupBy y Map.groupBy que en ese momento estaban en stage 3 de TC39. La firma devuelve Partial<Record<K, T[]>>, con el Partial marcando correctamente que no todas las claves del union aparecen necesariamente.
Es una conveniencia directa que elimina una dependencia de lodash en proyectos que solo la usaban para esto.
Rendimiento del compilador
Las notas de release mencionan menor consumo de memoria, builds incrementales más rápidos y mejor deduplicación de type instances. En un proyecto de diez archivos no notarás nada. En un monorepo con cientos de paquetes y un tsc --build que tardaba minutos, la diferencia es perceptible —no transformadora, pero suficiente para que el CI respire.
Lo que no trae y lo que puede romper
Los breaking changes son menores y puntuales:
- Homomorfismo más estricto en mapped type variations puede afectar a librerías con type gymnastics agresivo.
- Algunas firmas de
Function.prototype.bindse endurecen.
En código de aplicación la probabilidad de notarlo es prácticamente cero. Las ausencias de siempre (decorators estables completos, pipeline operator, pattern matching) siguen esperando movimientos en TC39.
Actualizar desde 5.3
En la abrumadora mayoría de proyectos la migración se reduce a tres pasos:
npm install -D typescript@5.4- Correr
tsc --noEmity comprobar si aparecen errores nuevos. - Si usas
@typescript-eslint, actualizar el parser a versión compatible en el mismo commit.
Vite, esbuild, swc, Next, Nuxt, SvelteKit y el resto del stack moderno no requieren cambios. Es una de las releases menos traumáticas de los últimos años. El proyecto está bien posicionado para aprovechar luego mejoras de ONNX Runtime en edge, donde TypeScript estricto en los tipos de tensores evita errores en producción.
Conclusión
TypeScript 5.4 no cambia cómo piensas sobre tipos, pero retira tres workarounds que llevabas años escribiendo sin darte cuenta. NoInfer convierte en una anotación trivial lo que antes requería dos type parameters o un conditional type oscuro. El narrowing preservado en closures elimina una categoría entera de casts molestos. Object.groupBy cierra la última excusa para importar lodash solo por agrupar una lista. Para un equipo activo, migrar pronto compensa: los patrones nuevos hacen el código más expresivo y los antiguos empiezan a oler a legacy más rápido de lo esperado.