TypeScript 5.4 y 5.5 han agregado mejoras en la inferencia de tipos, mejoras en el rendimiento y nuevos tipos de utilidad. En este articulo, explicamos estas nuevas funciones con ejemplos de codigo practicos.
Nuevas Funciones de TypeScript 5.4
Tipo de Utilidad NoInfer
NoInfer<T> es un nuevo tipo de utilidad que suprime la inferencia de parametros de tipo.
// NoInfer - Control de la inferencia de tipos
// Problema: Inferencia de tipos no deseada
function createState<T>(initial: T, allowed: T[]): T {
return allowed.includes(initial) ? initial : allowed[0];
}
// Se infiere como string en lugar de "red" | "blue"
const state1 = createState("red", ["red", "blue", "green"]);
// Solucion con NoInfer
function createStateFixed<T>(initial: T, allowed: NoInfer<T>[]): T {
return allowed.includes(initial) ? initial : allowed[0];
}
// Se infiere correctamente como "red" | "blue" | "green"
const state2 = createStateFixed("red", ["red", "blue", "green"]);
// Ejemplo practico: Manejador de eventos
type EventMap = {
click: { x: number; y: number };
keypress: { key: string };
scroll: { scrollY: number };
};
function addEventListener<K extends keyof EventMap>(
event: K,
handler: (data: NoInfer<EventMap[K]>) => void
): void {
// Implementacion
}
// El tipo del handler no se infiere de EventMap[K]
addEventListener("click", (data) => {
// data esta correctamente tipado como { x: number; y: number }
console.log(data.x, data.y);
});
Mejoras en el Narrowing dentro de Closures
El narrowing de tipos dentro de funciones y callbacks ha mejorado significativamente.
// Narrowing mejorado dentro de closures
function processData(value: string | number | null) {
// TypeScript 5.3 y anteriores: El narrowing se perdia dentro de callbacks
// TypeScript 5.4: El narrowing se mantiene
if (value === null) {
return;
}
// Aqui value es string | number
const handlers = {
// TS 5.4: El narrowing se mantiene dentro de closures
process: () => {
// value se reconoce como string | number
if (typeof value === "string") {
return value.toUpperCase();
}
return value.toFixed(2);
},
log: () => {
// El narrowing tambien se mantiene aqui
console.log(value);
}
};
return handlers.process();
}
// Narrowing despues de bifurcacion condicional
function example(arr: string[] | null) {
if (arr === null) {
return;
}
// TypeScript 5.4: arr es string[] incluso dentro del callback de map
arr.map((item) => {
// Se garantiza que arr no es null
console.log(arr.length); // OK
return item.toUpperCase();
});
}
// Ejemplo mas complejo
type Result<T> = { success: true; data: T } | { success: false; error: string };
function processResult<T>(result: Result<T>) {
if (!result.success) {
return null;
}
// result.data esta disponible
const transformers = {
// Se reconoce que result.success es true dentro del closure
transform: () => {
return result.data; // TS 5.4: OK
}
};
return transformers.transform();
}
Soporte de Tipos para Object.groupBy
Se ha agregado soporte de tipos para Object.groupBy y Map.groupBy de ES2024.
// Object.groupBy - Compatibilidad con ES2024
interface User {
id: string;
name: string;
role: 'admin' | 'user' | 'guest';
department: string;
}
const users: User[] = [
{ id: '1', name: 'Alice', role: 'admin', department: 'Engineering' },
{ id: '2', name: 'Bob', role: 'user', department: 'Engineering' },
{ id: '3', name: 'Charlie', role: 'user', department: 'Sales' },
{ id: '4', name: 'Diana', role: 'guest', department: 'Marketing' },
];
// Agrupar por rol
const byRole = Object.groupBy(users, (user) => user.role);
// Tipo: Partial<Record<'admin' | 'user' | 'guest', User[]>>
console.log(byRole.admin); // [{ id: '1', name: 'Alice', ... }]
console.log(byRole.user); // [{ id: '2', ... }, { id: '3', ... }]
// Agrupar por departamento
const byDepartment = Object.groupBy(users, (user) => user.department);
// Tipo: Partial<Record<string, User[]>>
// Map.groupBy - Retorna un Map
const byRoleMap = Map.groupBy(users, (user) => user.role);
// Tipo: Map<'admin' | 'user' | 'guest', User[]>
byRoleMap.forEach((users, role) => {
console.log(`${role}: ${users.length} usuarios`);
});
// Ejemplo practico: Agrupar por fecha
interface Order {
id: string;
amount: number;
date: Date;
}
function groupOrdersByMonth(orders: Order[]) {
return Object.groupBy(orders, (order) => {
const month = order.date.toISOString().slice(0, 7); // "2025-12"
return month;
});
}
Nuevas Funciones de TypeScript 5.5
Predicados de Tipo Inferidos
Los type guards ahora se infieren automaticamente del valor de retorno de las funciones.
// Predicados de Tipo Inferidos (Type Predicates)
// TS 5.4 y anteriores: Se requeria un predicado de tipo explicito
function isStringOld(value: unknown): value is string {
return typeof value === 'string';
}
// TS 5.5: El predicado de tipo se infiere automaticamente
function isString(value: unknown) {
return typeof value === 'string';
}
// Tipo inferido: (value: unknown) => value is string
// Uso con filtrado de arrays
const mixed: (string | number | null)[] = ['a', 1, null, 'b', 2];
// TS 5.4 y anteriores: El resultado de filter sigue siendo (string | number | null)[]
const stringsOld = mixed.filter((x): x is string => typeof x === 'string');
// TS 5.5: Se infiere automaticamente como string[]
const strings = mixed.filter((x) => typeof x === 'string');
// Tipo: string[]
// Eliminacion de null tambien es automatica
const notNull = mixed.filter((x) => x !== null);
// Tipo: (string | number)[]
// Ejemplo complejo
interface User {
id: string;
name: string;
email?: string;
}
const users: (User | null)[] = [
{ id: '1', name: 'Alice', email: 'alice@example.com' },
null,
{ id: '2', name: 'Bob' },
];
// El tipo se reduce automaticamente
const validUsers = users.filter((user) => user !== null && user.email);
// Tipo: User[] (solo usuarios con email)
Verificacion de Sintaxis de Expresiones Regulares
Se ha agregado verificacion de sintaxis para literales de expresiones regulares.
// Verificacion de sintaxis de expresiones regulares
// TS 5.5: Las expresiones regulares invalidas son errores de compilacion
// Error: Secuencia de escape invalida
// const invalid1 = /\p/;
// Error: Clase de caracteres incompleta
// const invalid2 = /[a-/;
// Error: Cuantificador invalido
// const invalid3 = /a{}/;
// Expresiones regulares validas
const email = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const phone = /^\d{3}-\d{4}-\d{4}$/;
const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
// Escapes de propiedades Unicode (requiere flag u)
const japanese = /\p{Script=Hiragana}+/u;
const emoji = /\p{Emoji}/u;
// Grupos de captura con nombre
const datePattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = '2025-12-02'.match(datePattern);
if (match?.groups) {
const { year, month, day } = match.groups;
console.log(year, month, day); // "2025", "01", "02"
}
Declaraciones Aisladas (Isolated Declarations)
La opcion isolatedDeclarations permite acelerar la generacion de declaraciones de tipos.
// tsconfig.json
{
"compilerOptions": {
"isolatedDeclarations": true,
"declaration": true
}
}
// Con isolatedDeclarations: true,
// es necesario especificar explicitamente los tipos de retorno de funciones y tipos de variables
// ❌ Error: Se requiere el tipo de retorno
// export function add(a: number, b: number) {
// return a + b;
// }
// ✅ OK: Tipo de retorno explicito
export function add(a: number, b: number): number {
return a + b;
}
// ❌ Error: Se requiere el tipo de la variable
// export const config = { port: 3000, host: 'localhost' };
// ✅ OK: Tipo explicito
export const config: { port: number; host: string } = {
port: 3000,
host: 'localhost'
};
// Beneficios:
// - Posibilidad de paralelizacion del build
// - Generacion de .d.ts sin verificacion de tipos
// - Build rapido en monorepos
Soporte de Tipos para Nuevos Metodos de Set
Se ha agregado soporte de tipos para los metodos de Set de ES2024.
// Nuevos metodos de Set
const setA = new Set([1, 2, 3, 4]);
const setB = new Set([3, 4, 5, 6]);
// union: Union de conjuntos
const union = setA.union(setB);
console.log([...union]); // [1, 2, 3, 4, 5, 6]
// intersection: Interseccion de conjuntos
const intersection = setA.intersection(setB);
console.log([...intersection]); // [3, 4]
// difference: Diferencia de conjuntos
const difference = setA.difference(setB);
console.log([...difference]); // [1, 2]
// symmetricDifference: Diferencia simetrica
const symmetricDiff = setA.symmetricDifference(setB);
console.log([...symmetricDiff]); // [1, 2, 5, 6]
// isSubsetOf: Verificacion de subconjunto
const subset = new Set([2, 3]);
console.log(subset.isSubsetOf(setA)); // true
// isSupersetOf: Verificacion de superconjunto
console.log(setA.isSupersetOf(subset)); // true
// isDisjointFrom: Verificacion de conjuntos disjuntos
const setC = new Set([7, 8, 9]);
console.log(setA.isDisjointFrom(setC)); // true
// Ejemplo practico: Filtrado de etiquetas
interface Article {
id: string;
title: string;
tags: Set<string>;
}
function filterByTags(articles: Article[], requiredTags: Set<string>): Article[] {
return articles.filter(article =>
requiredTags.isSubsetOf(article.tags)
);
}
Mejoras de Rendimiento
Aceleracion de la Verificacion de Tipos
// Mejoras de rendimiento en TypeScript 5.4/5.5
// 1. Verificacion de tipos monomorfizada
// - Procesamiento acelerado de objetos con la misma forma
// 2. Optimizacion de tipos condicionales
type IsString<T> = T extends string ? true : false;
// Mejora en el almacenamiento en cache interno
// 3. Optimizacion del build con referencias de proyecto
// tsconfig.json
{
"compilerOptions": {
"incremental": true,
"composite": true,
// Agregado en 5.5
"isolatedDeclarations": true
},
"references": [
{ "path": "./packages/core" },
{ "path": "./packages/utils" }
]
}
// 4. Mejora en la capacidad de respuesta del editor
// - Sugerencias de auto-importacion aceleradas
// - Operaciones de refactorizacion mejoradas
Patrones de Tipos Practicos
Utilidades de Tipos Mejoradas
// Patrones de tipos practicos
// 1. DeepPartial mas seguro
type DeepPartial<T> = T extends object
? { [P in keyof T]?: DeepPartial<T[P]> }
: T;
interface Config {
server: {
port: number;
host: string;
ssl: {
enabled: boolean;
cert: string;
};
};
database: {
url: string;
pool: number;
};
}
// Actualizacion parcial de configuracion
function updateConfig(partial: DeepPartial<Config>): Config {
// Logica de fusion
return {} as Config;
}
updateConfig({
server: {
ssl: {
enabled: true
}
}
});
// 2. Sistema de eventos con seguridad de tipos
type EventMap = {
'user:login': { userId: string; timestamp: Date };
'user:logout': { userId: string };
'order:created': { orderId: string; total: number };
};
class TypedEventEmitter<T extends Record<string, unknown>> {
private handlers = new Map<keyof T, Set<(data: any) => void>>();
on<K extends keyof T>(event: K, handler: (data: T[K]) => void): () => void {
if (!this.handlers.has(event)) {
this.handlers.set(event, new Set());
}
this.handlers.get(event)!.add(handler);
return () => this.handlers.get(event)?.delete(handler);
}
emit<K extends keyof T>(event: K, data: T[K]): void {
this.handlers.get(event)?.forEach(handler => handler(data));
}
}
const emitter = new TypedEventEmitter<EventMap>();
// Manejo de eventos con seguridad de tipos
emitter.on('user:login', (data) => {
// data es { userId: string; timestamp: Date }
console.log(data.userId, data.timestamp);
});
// 3. Propiedades requeridas condicionales
type RequireAtLeastOne<T, Keys extends keyof T = keyof T> =
Pick<T, Exclude<keyof T, Keys>> &
{ [K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>> }[Keys];
interface SearchParams {
query?: string;
categoryId?: string;
tags?: string[];
}
// Al menos una condicion de busqueda es requerida
type ValidSearchParams = RequireAtLeastOne<SearchParams, 'query' | 'categoryId' | 'tags'>;
function search(params: ValidSearchParams) {
// Implementacion
}
// OK
search({ query: 'test' });
search({ categoryId: '123' });
search({ query: 'test', tags: ['a', 'b'] });
// Error
// search({}); // Se requiere al menos una condicion
Guia de Migracion
Migracion de 5.3 a 5.4/5.5
# Actualizacion
npm install typescript@latest
# Verificacion de tipos
npx tsc --noEmit
# Puntos de correccion comunes
// 1. Adaptacion al nuevo comportamiento de narrowing
// Algunos codigos ahora infieren tipos correctamente,
// y las aserciones de tipo que antes eran necesarias pueden volverse innecesarias
// Antes (TS 5.3)
function example(value: string | null) {
if (value === null) return;
const fn = () => {
// Se requeria value as string
return (value as string).toUpperCase();
};
}
// Despues (TS 5.4+)
function exampleNew(value: string | null) {
if (value === null) return;
const fn = () => {
// Asercion de tipo no necesaria
return value.toUpperCase();
};
}
// 2. Uso de Object.groupBy
// Se requiere verificacion de la configuracion de lib
// tsconfig.json
{
"compilerOptions": {
"lib": ["ES2024"] // o "ESNext"
}
}
Resumen
TypeScript 5.4/5.5 ha traido mejoras significativas en la inferencia de tipos y el rendimiento.
Principales Nuevas Funciones
| Funcion | Version | Impacto |
|---|---|---|
| NoInfer | 5.4 | Control de inferencia de tipos |
| Narrowing en closures | 5.4 | Mejora en la calidad del codigo |
| Object.groupBy | 5.4 | Compatibilidad con ES2024 |
| Predicados de tipo inferidos | 5.5 | Mejora en filter, etc. |
| isolatedDeclarations | 5.5 | Aceleracion del build |
| Verificacion de regex | 5.5 | Deteccion de errores mejorada |
Recomendaciones de Actualizacion
- Nuevos proyectos: Adoptar la ultima version
- Proyectos existentes: Migrar gradualmente
- Proyectos grandes: Aprovechar isolatedDeclarations
Con la evolucion de TypeScript, es posible escribir codigo mas seguro y eficiente.