TypeScript: ¿Por qué preferir Maps sobre Enums?

Aunque los enum en TypeScript pueden parecer una elección natural para representar un conjunto fijo de valores, tienen limitaciones importantes que pueden afectar la mantenibilidad y el rendimiento del código. Veamos por qué los Map son a menudo una mejor alternativa.

El problema con los Enums

Los enums tienen varias desventajas importantes que se hacen evidentes al desarrollar aplicaciones complejas:

  1. Rigidez: No se pueden modificar dinámicamente
  2. Rendimiento: Genera código JavaScript verbose
  3. Tipado complejo: Dificultades con la introspección y uniones de tipos
  4. Tamaño del bundle: Impacto en el tamaño final del código

Consideremos este ejemplo:

typescript
1// Código TypeScript
2enum Status {
3 Active = 'ACTIVE',
4 Inactive = 'INACTIVE',
5}
6
7// Código JavaScript transpilado
8var Status = {
9 Active: 'ACTIVE',
10 Inactive: 'INACTIVE',
11 ACTIVE: 'Active',
12 INACTIVE: 'Inactive',
13};
14(function (Status) {
15 Status['Active'] = 'ACTIVE';
16 Status['Inactive'] = 'INACTIVE';
17})(Status || (Status = {}));

Este código transpilado genera:

  • Un objeto con propiedades espejo (clave ↔ valor)
  • Una IIFE (Immediately Invoked Function Expression) superflua
  • Referencia doble para cada valor
  • Código adicional que impacta el rendimiento

La solución con Maps

Los Map y objetos TypeScript ofrecen un enfoque más elegante y flexible:

typescript
1// Solución con const y tipos
2const Status = {
3 Active: 'ACTIVE',
4 Inactive: 'INACTIVE',
5} as const;
6
7// Tipo inferido automáticamente
8type Status = (typeof Status)[keyof typeof Status];
9
10// Código transpilado simple y eficiente
11const Status = {
12 Active: 'ACTIVE',
13 Inactive: 'INACTIVE',
14};

Ventajas de los Maps

  1. Seguridad de tipos
typescript
1function processStatus(status: Status) {
2 // Error de compilación si el status es inválido
3 console.log(status);
4}
5
6// Validación en tiempo de ejecución
7const isValidStatus = (status: string): status is Status =>
8 Object.values(Status).includes(status as Status);
  1. Extensibilidad
typescript
1// Adición dinámica posible
2const ExtendedStatus = {
3 ...Status,
4 Pending: 'PENDING' as const,
5};
  1. Patrones avanzados
typescript
1// Configuración asociada
2const StatusConfig = {
3 [Status.Active]: {
4 color: 'green',
5 label: 'Activo',
6 icon: 'check',
7 },
8 [Status.Inactive]: {
9 color: 'red',
10 label: 'Inactivo',
11 icon: 'cross',
12 },
13} as const;
14
15// Tipo utilitario
16type StatusConfig = {
17 [K in Status]: {
18 color: string;
19 label: string;
20 icon: string;
21 };
22};
23
24// Función helper tipada
25function getStatusConfig(status: Status) {
26 return StatusConfig[status];
27}

Casos de uso prácticos

1. Endpoints de API

typescript
1const ApiEndpoints = {
2 Users: '/api/users',
3 Products: '/api/products',
4 Orders: '/api/orders',
5} as const;
6
7type Endpoint = (typeof ApiEndpoints)[keyof typeof ApiEndpoints];
8
9// Tipado automático de URLs
10function fetchData(endpoint: Endpoint) {
11 return fetch(endpoint);
12}

2. Gestión de estados

typescript
1const LoadingState = {
2 Idle: 'IDLE',
3 Loading: 'LOADING',
4 Success: 'SUCCESS',
5 Error: 'ERROR',
6} as const;
7
8type LoadingState = typeof LoadingState[keyof typeof LoadingState];
9
10function handleState(state: LoadingState) {
11 switch (state) {
12 case LoadingState.Loading:
13 return <Spinner />;
14 case LoadingState.Error:
15 return <ErrorMessage />;
16 // TypeScript verifica exhaustividad
17 }
18}

Tabla comparativa detallada

Una comparación detallada entre Enums y Maps:

Seguridad de tipos

  • Enum: Básica (✓)
  • Map: Avanzada con inferencia de tipos (✓✓)

Rendimiento en tiempo de ejecución

  • Enum: Menos eficiente (✗)
  • Map: Más eficiente (✓)

Tamaño del bundle

  • Enum: Mayor (✗)
  • Map: Menor (✓)

Extensibilidad

  • Enum: Difícil de extender (✗)
  • Map: Fácilmente extensible (✓)

Introspección

  • Enum: Capacidades limitadas
  • Map: Capacidades completas

Compatibilidad TypeScript strict

  • Enum: Compatible (✓)
  • Map: Totalmente compatible con más funciones (✓✓)

Mantenimiento

  • Enum: Más complejo de mantener
  • Map: Más fácil de mantener

Mejores prácticas

  1. Nombres explícitos
typescript
1// Preferido
2const HttpStatus = {
3 Ok: 200,
4 NotFound: 404,
5 ServerError: 500,
6} as const;
7
8// Evitar
9const Status = {
10 a: 200,
11 b: 404,
12 c: 500,
13} as const;
  1. Union Types
typescript
1// Creación de uniones type-safe
2type HttpSuccessStatus = 200 | 201 | 204;
3type HttpErrorStatus = 400 | 401 | 404 | 500;
4type HttpStatus = HttpSuccessStatus | HttpErrorStatus;
  1. Validación en tiempo de ejecución
typescript
1function isHttpSuccess(status: number): status is HttpSuccessStatus {
2 return [200, 201, 204].includes(status);
3}

Conclusión

Los Maps ofrecen una alternativa más robusta a los enums con:

  • Tipado más preciso y flexible
  • Mejor rendimiento en tiempo de ejecución
  • Mantenimiento simplificado
  • Mejor compatibilidad con el ecosistema JavaScript

Para profundizar en estos conceptos, consulta:

¡No dudes en contribuir a este artículo compartiendo tus experiencias!

Comparte este artículo


Sébastien Timoner

Sébastien TIMONER

Desarrollador Líder
Experto en Desarrollo a Medida
Aix-en-Provence, France

Experto en desarrollo web y gestión de equipos técnicos, me especializo en la creación y optimización de soluciones digitales de alto rendimiento. Gracias a un profundo dominio de tecnologías modernas como React.js, Node.js, TypeScript y Symfony, garantizo el éxito de proyectos SaaS complejos, desde el diseño hasta la implementación, para empresas de diversos sectores, dentro de offroadLabs.

En offroadLabs, ofrezco servicios de desarrollo a medida, combinando experiencia técnica y enfoque colaborativo. Ya sea para crear una solución SaaS innovadora, modernizar una aplicación existente o acompañar el desarrollo de habilidades de un equipo, me comprometo a proporcionar soluciones robustas y eficientes, adaptadas a las necesidades específicas de cada proyecto.

Estoy disponible para proyectos en la zona de Aix-en-Provence o en modalidad totalmente remota.