TypeScript 5.x ha añadido en cada versión menor características que cambian cómo se gestiona el ciclo de vida de recursos, la herencia de clases y la importación de módulos. Esta guía cubre las novedades más relevantes de TypeScript 5.0 a 5.8: using y await using para la gestión automática de recursos, override para heredar con seguridad, import attributes para importar JSON y módulos con metadatos, y la inferencia automática de type predicates introducida en 5.5.
using y await using: gestión automática de recursos
TypeScript 5.2 implementó la propuesta ECMAScript Explicit Resource Management. Cualquier objeto que implemente Symbol.dispose (o Symbol.asyncDispose) se puede declarar con using y TypeScript garantiza que dispose() se llama al salir del bloque, similar a try/finally o el with de Python:
class ConexionBD {
private id = Math.random();
consultar(sql: string): string[] {
console.log(`[${this.id}] Ejecutando: ${sql}`);
return [];
}
[Symbol.dispose]() {
console.log(`[${this.id}] Conexión cerrada`);
}
}
function procesarDatos() {
using conn = new ConexionBD(); // se cerrará al salir de la función
const datos = conn.consultar("SELECT * FROM usuarios");
return datos;
// Al salir: conn[Symbol.dispose]() se llama automáticamente
}
// Versión asíncrona:
class ArchivoAsync {
async [Symbol.asyncDispose]() {
await flushBuffer();
console.log("Archivo cerrado");
}
}
async function escribir() {
await using archivo = new ArchivoAsync();
// archivo se cierra con await cuando salga de la función
}
override: herencia segura
Con noImplicitOverride: true en el tsconfig, TypeScript exige que los métodos que sobreescriben al padre estén marcados con override. Esto evita que un cambio en la clase base rompa silenciosamente las subclases:
class Animal {
mover(distancia: number): void {
console.log(`Moviéndose ${distancia}m`);
}
hablar(): void { /* ... */ }
}
class Perro extends Animal {
override mover(distancia: number): void {
console.log("Corriendo...");
super.mover(distancia);
}
// override hablarMal(): void {} // Error: no existe en Animal
// hablar(): void {} // Error con noImplicitOverride: falta override
}
import attributes con with
TypeScript 5.3 adoptó la sintaxis with para los import attributes (antes assert), que permite anotar imports con metadatos que el runtime puede usar para validar el tipo de módulo:
// Importar JSON (requiere soporte en el runtime/bundler):
import datos from "./config.json" with { type: "json" };
// datos tiene el tipo exacto del JSON inferido por TypeScript
// Importar CSS (con bundlers que soporten import attributes):
import estilos from "./componente.css" with { type: "css" };
// Re-exportar con atributos:
export { default as config } from "./config.json" with { type: "json" };
// En dynamic imports:
const modulo = await import("./plugin.js", { with: { type: "json" } });
Inferencia de type predicates en TS 5.5
TypeScript 5.5 introdujo la inferencia automática de type predicates en funciones cuyo cuerpo hace narrowing. Antes había que escribir x is Tipo explícitamente; ahora TypeScript lo infiere:
// Antes de 5.5: había que anotar el predicado manualmente const esString = (x: unknown): x is string => typeof x === "string"; // Desde 5.5: TypeScript infiere que es un type predicate const esString55 = (x: unknown) => typeof x === "string"; // TypeScript infiere: (x: unknown) => x is string ? // Muy útil con Array.filter: const valores = [1, "dos", 3, "cuatro", null]; const soloStrings = valores.filter(x => typeof x === "string"); // Antes: (string | number | null)[] // Desde 5.5: string[] ?
Otras mejoras destacadas en 5.x
// TS 5.0: const type parameters (ver artículo anterior)
// TS 5.1: tipos de retorno separados para getters y setters relacionados
class Temperatura {
#celsius: number = 0;
get grados(): number { return this.#celsius; }
// Antes: getter y setter debían tener el mismo tipo
// Desde 5.1: el setter puede aceptar un tipo diferente
set grados(valor: number | string) {
this.#celsius = typeof valor === "string" ? parseFloat(valor) : valor;
}
}
// TS 5.4: NoInfer utility type
function porDefecto<T>(lista: T[], fallback: NoInfer<T>): T {
return lista[0] ?? fallback;
}
// fallback no participa en la inferencia de T, solo debe ser compatible
// TS 5.5: isolated declarations
// Con "isolatedDeclarations": true, cada archivo debe tener tipos explícitos
// para sus exports, lo que permite paralelizar la generación de .d.ts
Las versiones 5.x de TypeScript han añadido herramientas que hacen el código más seguro (override, using), más expresivo (const type parameters, satisfies) y más rápido de analizar (isolated declarations). Mantenerse al día con estas versiones menores suele costar poco la mayoría son compatibles hacia atrás y el beneficio en tipado y rendimiento del IDE se nota desde el primer día.
Imagen: Pexels / Markus Spiske
