Por qué deberías evitar enums en TypeScript para tus proyectos
¿Debemos evitar enums en TypeScript?
Tiempo estimado de lectura: 3 min
- Los enums generan código en runtime y rompen la promesa de tipos que desaparecen.
- Alternativas preferibles: union types para tipos puros; objetos
as constpara valores en runtime. - const enum inlining es frágil y rompe flujos modernos como isolatedModules.
- Plan práctico: encontrar enums, priorizar UI/API, sustituir y añadir regla ESLint.
Introducción
¿De verdad sigues escribiendo enums en tus proyectos nuevos? Si tu respuesta es sí, hay que hablar en serio.
En TypeScript los enums son una característica heredada con promesas bonitas y costes escondidos. La pregunta no es moral: es práctica. ¿Te importa el tamaño del bundle, la previsibilidad del runtime y la compatibilidad con herramientas modernas?
Entonces sí: evita enums por defecto.
Resumen rápido (lectores con prisa)
Los enums generan código JavaScript en runtime; prefieres union types si sólo necesitas tipos, y objetos as const si necesitas valores en runtime. const enum hace inlining pero es frágil con herramientas modernas.
¿Debemos evitar enums en TypeScript? — la explicación breve
TypeScript existe para dar tipos que desaparecen al compilar. Interfaces y type unions se evaporan. Los enums no: generan código JavaScript en runtime. Eso rompe la promesa de “tipos que no pesan”.
Mira esto:
enum Status { Active, Inactive, Pending }
Se transforma en una IIFE que crea un objeto y mapas reversos. Resultado: bytes extra, más complejidad para los bundlers y más superficie para errores silenciosos.
Si quieres leer la doc oficial.
Por qué los enums son problemáticos hoy
- Generan código en runtime. No es sólo “feo”: es carga real en producción.
- Tree-shaking menos efectivo. Esa IIFE puede sobrevivir al proceso de eliminación de código muerto (ver guía de Webpack).
- Los enums numéricos permiten reverse mapping y asignaciones inesperadas: puedes acabar con números inválidos sin que TypeScript te lo grite.
const enumhace inlining, pero rompe flujos modernos con isolatedModules (esbuild, SWC, tsup) — herramientas que usamos para acelerar builds (esbuild, Vite).
Qué usar en su lugar (patrones que sí funcionan)
No te mando a la guerra sin armas. Hay alternativas simples, robustas y alineadas con el ecosistema JS.
1) Union Types (cuando sólo quieres limitar valores)
type Status = 'active' | 'inactive' | 'pending';
function setStatus(s: Status) { /* ... */ }
- Cero runtime.
- Tipado claro y autocumplido en IDE.
- Perfecto para props, APIs y funciones.
2) Objetos as const (cuando necesitas valores en runtime)
const STATUS = {
ACTIVE: 'active',
INACTIVE: 'inactive',
PENDING: 'pending'
} as const;
type Status = typeof STATUS[keyof typeof STATUS];
- Tienes objeto para iterar (Object.values).
- Tipos literales extraídos automáticamente.
- JavaScript simple en runtime: nada de IIFEs raras.
3) Branded Types (si necesitas nominalidad)
Cuando realmente quieres que dos tipos similares no sean intercambiables, usa branded types. Es más verboso, pero evita los problemas del tipado nominal que los enums intentan resolver con coste.
`const enum`: la promesa rota
Sí, const enum elimina el objeto y hace inlining. Suena perfecto. En la práctica es frágil:
- Rompe con
isolatedModules. - Empeora sourcemaps y debugging.
- Depende del flujo de compilación; en monorepos o builds parciales, es una bomba de tiempo.
Conclusión: no es solución universal. Evitarlo también es sensato.
Qué hacer en tu codebase (plan práctico)
- Busca: grep por
enumen tu repo. - Prioriza: enums usados en UI y API surface primero.
- Sustituye:
- Si no necesitas iteración: cambia por union type.
- Si necesitas runtime: cambia por objeto
as const.
- Añade regla ESLint para evitar futuros enums:
{ "@typescript-eslint/no-restricted-syntax": [ "error", { "selector": "TSEnumDeclaration", "message": "Usa union types u objetos as const en lugar de enums" } ] } - Prueba, mide bundle y sourcemaps. Verás la diferencia.
¿Cuándo sí considerar un enum?
Muy raras veces. Casos válidos:
- Integración con código legacy que usa enums.
- SDKs donde los valores numéricos son parte del contrato público (protocolos).
- Interoperabilidad con librerías externas que exigen enums.
Pero si arrancas un proyecto nuevo, el path claro es evitar enums.
Cierre con criterio
Los enums ya no son la herramienta “obvia” que eran. Son una reliquia que introduce deuda técnica donde no la necesitas. Prefiere union types o as const. Son más predecibles, más rápidos y más amables con las herramientas modernas.
Revisa tu código: haz una búsqueda rápida de enum. Si aparecen, planifica una migración por sprint. Tu bundle —y tu tranquilidad— lo agradecerán.
FAQ
¿Por qué los enums generan código en runtime?
Porque TypeScript traduce los enums a objetos JavaScript (a menudo via una IIFE) que existen en el runtime para soportar features como reverse mapping y valores numéricos.
¿No puedo usar const enum para evitar el código extra?
const enum hace inlining eliminando el objeto, pero es frágil: rompe con isolatedModules, complica sourcemaps y depende de flujos de compilación coherentes en monorepos y builds parciales.
¿Qué gana mi bundle evitando enums?
Menos bytes en el runtime, mejor posibilidad de tree-shaking y menos complejidad para los bundlers, lo que suele traducirse en bundles más pequeños y builds más predecibles.
¿Los objetos as const permiten iteración?
Sí. Un objeto declarado con as const se puede iterar con Object.values() o métodos similares y además puedes extraer los tipos literales automáticamente con typeof.
¿Cómo detecto enums en un repo grande?
Haz una búsqueda por la palabra clave enum (por ejemplo con grep) y prioriza los usos en la UI y la surface de la API.
¿Cuándo debo mantener un enum existente?
Cuando hay integración con código legacy que depende de enums, cuando los valores numéricos son parte de un contrato público (SDKs/protocolos) o para interoperabilidad con librerías externas que exigen enums.
