Guia Completa de las Nuevas Funciones de TypeScript 5.4/5.5 - Evolucion de la Inferencia de Tipos y Rendimiento

2025.12.02

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

FuncionVersionImpacto
NoInfer5.4Control de inferencia de tipos
Narrowing en closures5.4Mejora en la calidad del codigo
Object.groupBy5.4Compatibilidad con ES2024
Predicados de tipo inferidos5.5Mejora en filter, etc.
isolatedDeclarations5.5Aceleracion del build
Verificacion de regex5.5Deteccion 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.

Enlaces de Referencia

← Volver a la lista