TypeScript 5.x: las características que cambian cómo escribes código
Las versiones 5.x de TypeScript trajeron mejoras que van más allá del sistema de tipos. Decoradores estándar, const type parameters, y mejoras en el narrowing reducen el boilerplate y hacen el código más expresivo.
TypeScript 5 llegó con un conjunto de cambios que muchos equipos todavía no han adoptado completamente. No son solo correcciones —son nuevas formas de expresar intención en el sistema de tipos que reducen el código de workaround y mejoran la experiencia de desarrollo. Esta guía cubre las más impactantes para código de producción.
Decoradores estándar (TC39 Stage 3)
Después de años con decoradores experimentales que requerían experimentalDecorators: true y generaban advertencias, TypeScript 5 implementó el estándar TC39. La diferencia no es solo sintáctica —la semántica cambió:
// Decorador de clase estándar
function logClass(target: typeof MyClass, context: ClassDecoratorContext) {
console.log(`Clase ${context.name} creada`)
}
@logClass
class MyService {
// ...
}
// Decorador de método con acceso al contexto
function memoize(target: Function, context: ClassMethodDecoratorContext) {
const cache = new Map()
return function(this: unknown, ...args: unknown[]) {
const key = JSON.stringify(args)
if (!cache.has(key)) {
cache.set(key, target.apply(this, args))
}
return cache.get(key)
}
}Los frameworks que usaban decoradores experimentales (NestJS, TypeORM) están migrando gradualmente al nuevo estándar. Verifica la versión de tu framework antes de cambiar la configuración.
const type parameters
Antes de TS 5, cuando pasabas un array o un objeto literal como argumento genérico, TypeScript lo infería como el tipo widened (ampliado). Con const en el type parameter, infiere el tipo literal exacto:
// Sin const: inferido como string[]
function firstItem<T>(arr: T[]): T {
return arr[0]
}
const x = firstItem(["a", "b", "c"]) // tipo: string
// Con const: inferido como "a" | "b" | "c"
function firstItemConst<const T>(arr: T[]): T {
return arr[0]
}
const y = firstItemConst(["a", "b", "c"]) // tipo: "a"Útil para builders, configuraciones y cualquier función que debe preservar el tipo literal de sus argumentos.
Mejoras en satisfies + narrowing combinados
El operador satisfies (introducido en 4.9) combinado con las mejoras de narrowing de 5.x permite validar que un objeto cumple una interfaz sin perder los tipos literales específicos:
type Config = {
env: "development" | "production" | "test"
port: number
features: Record<string, boolean>
}
const config = {
env: "production",
port: 3000,
features: { darkMode: true, newDashboard: false }
} satisfies Config
// config.env tiene tipo "production" (no "development" | "production" | "test")
// config.features.darkMode tiene tipo booleanverbatimModuleSyntax y la claridad en imports
Con verbatimModuleSyntax: true en tsconfig.json, TypeScript obliga a marcar explícitamente los imports de tipos:
// ✅ Correcto con verbatimModuleSyntax
import type { User } from "./types"
import { createUser } from "./user-service"
// ❌ Error: no se sabe en tiempo de compilación si User es un tipo o un valor
import { User, createUser } from "./types"Esto elimina una clase entera de bugs sutiles donde los imports de tipos de TypeScript sobreviven al bundle y causan errores en runtime, especialmente en proyectos con ESM puro.
Adoptar estas características progresivamente en un proyecto existente es seguro —son opt-in y backward compatible. El mayor beneficio suele venir de const type parameters en utilidades genéricas y de verbatimModuleSyntax para proyectos nuevos con ESM.