TypeScript sigue evolucionando a un ritmo frenético. Las versiones 5.x han traído cambios que van más allá de las mejoras de rendimiento: redefinen patrones que llevamos años usando.
Tabla de contenidos
Open Tabla de contenidos
Decoradores estándar (TC39 Stage 3)
Por fin. Después de años con la versión experimental, TypeScript 5.0 adoptó los decoradores estándar de TC39. La sintaxis es similar pero la semántica ha cambiado bastante.
// Decorador de clase — antes (experimental)
@sealed
class OldClass { ... }
// Decorador estándar — TS 5.x
function logged<T extends new (...args: unknown[]) => unknown>(
target: T,
_ctx: ClassDecoratorContext,
) {
return class extends target {
constructor(...args: unknown[]) {
super(...args);
console.log(`[LOG] Instancia de ${target.name} creada`);
}
};
}
@logged
class UserService {
constructor(private db: Database) {}
}decorators.ts
Decoradores de método y acceso
function measure(_target: unknown, ctx: ClassMethodDecoratorContext) {
const name = String(ctx.name);
return function (this: unknown, ...args: unknown[]) {
const start = performance.now();
const result = (this as Record<string, Function>)[name](...args);
const result = Reflect.apply(
_target as Function,
this,
args
);
console.log(`${name} tardó ${performance.now() - start}ms`);
return result;
};
}
class ReportService {
@measure
async generatePDF(id: string) {
/* ... */
}
}method-decorator.ts
Parámetros de tipo const
Antes necesitabas usar as const en cada llamada para inferir tuplas literales. Ahora puedes declararlo en el genérico:
// Antes: inferido como string[]
function head<T>(arr: T[]) {
return arr[0];
}
head(["a", "b"]); // tipo: string
// Ahora: inferido como el literal exacto
function head<const T extends readonly unknown[]>(arr: T) {
return arr[0];
}
head(["a", "b"] as const); // tipo: "a"
head(["a", "b"]); // tipo: "a" ← funciona sin as constconst-type-params.ts
Operador satisfies (consolidado)
Introducido en la 4.9 pero ya forma parte del flujo diario. Permite validar que un valor cumple con un tipo sin “ensancharlo” (widening):
type Palette = {
red: [number, number, number] | string;
green: [number, number, number] | string;
blue: [number, number, number] | string;
};
const palette = {
red: [255, 0, 0],
green: "#00ff00",
blue: [0, 0, 255],
} satisfies Palette;
// Ahora TypeScript sabe que red es una tupla, no un string
palette.red.at(0); // ✓ — antes daba errorsatisfies.ts
Mejoras en la inferencia de infer
// Extraer el tipo de retorno filtrado por restricción
type ReturnIfString<T> = T extends () => infer R extends string
? R
: never;
type A = ReturnIfString<() => "hola">; // "hola"
type B = ReturnIfString<() => number>; // neverinfer-extends.ts
Rendimiento: modo --incremental y --composite
TS 5.x optimizó las compilaciones incrementales. En proyectos grandes la mejora puede ser de hasta 3 veces:
{
"compilerOptions": {
"composite": true,
"incremental": true,
"tsBuildInfoFile": ".tsbuildinfo",
"moduleResolution": "bundler"
}
}tsconfig.json
Consejo: combina
compositecon referencias de proyecto (references) para monorepos. Cada paquete solo compilará lo que haya cambiado.
Resumen rápido
| Funcionalidad | Versión | Impacto |
|---|---|---|
| Decoradores estándar | 5.0 | Alto — reemplaza a los experimentales |
Parámetros de tipo const | 5.0 | Medio — menos as const |
satisfies | 4.9 / consolidado en 5.x | Alto — tipado más expresivo |
infer ... extends | 5.x | Medio — tipos condicionales más precisos |
| Mejora en build incremental | 5.x | Alto en monorepos |
Traducido del original (Andrés Ujpán)