Enums

Intermedio
TypeScript
TypeScript
Actualizado: 07/05/2025

¡Desbloquea el curso completo!

IA
Ejercicios
Certificado
Entrar

Enums numéricos

Los enumerados (o enums) en TypeScript proporcionan una forma de definir un conjunto de constantes con nombre. Los enums numéricos son el tipo más básico y común, donde cada miembro recibe automáticamente un valor numérico.

Un enum numérico se define utilizando la palabra clave enum seguida del nombre del enum y un bloque de código que contiene los miembros del enum. Por defecto, TypeScript asigna valores numéricos secuenciales comenzando desde 0.

enum Direccion {
  Norte,    // 0
  Sur,      // 1
  Este,     // 2
  Oeste     // 3
}

// Uso del enum
let miDireccion: Direccion = Direccion.Norte;
console.log(miDireccion);  // Imprime: 0

En este ejemplo, cada miembro del enum Direccion recibe automáticamente un valor numérico incremental. El primer miembro (Norte) tiene el valor 0, el segundo (Sur) tiene el valor 1, y así sucesivamente.

Asignación manual de valores

Podemos asignar valores específicos a los miembros del enum, lo que nos permite controlar exactamente qué número representa cada constante:

enum CodigosHTTP {
  OK = 200,
  NotFound = 404,
  InternalServerError = 500,
  BadRequest = 400,
  Unauthorized = 401
}

function verificarRespuesta(codigo: number): void {
  if (codigo === CodigosHTTP.OK) {
    console.log("La solicitud fue exitosa");
  } else if (codigo === CodigosHTTP.NotFound) {
    console.log("Recurso no encontrado");
  }
}

verificarRespuesta(200);  // Imprime: "La solicitud fue exitosa"

En este caso, hemos asignado explícitamente los códigos HTTP estándar a cada miembro del enum.

Inicialización parcial

También es posible inicializar solo algunos miembros del enum. Los miembros no inicializados tomarán valores incrementales basados en el valor del miembro anterior:

enum NivelesAcceso {
  Usuario = 1,
  Editor,       // 2 (automáticamente)
  Administrador = 10,
  SuperAdmin    // 11 (automáticamente)
}

console.log(NivelesAcceso.Usuario);      // 1
console.log(NivelesAcceso.Editor);       // 2
console.log(NivelesAcceso.Administrador); // 10
console.log(NivelesAcceso.SuperAdmin);   // 11

En este ejemplo, Editor recibe automáticamente el valor 2 (incrementando desde el valor de Usuario), y SuperAdmin recibe el valor 11 (incrementando desde el valor de Administrador).

Acceso bidireccional

Una característica única de los enums numéricos en TypeScript es que permiten el acceso bidireccional: podemos acceder al valor numérico a partir del nombre, y también al nombre a partir del valor numérico:

enum Temporada {
  Primavera,  // 0
  Verano,     // 1
  Otoño,      // 2
  Invierno    // 3
}

let estacion: Temporada = Temporada.Verano;
console.log(estacion);                // Imprime: 1
console.log(Temporada[estacion]);     // Imprime: "Verano"
console.log(Temporada[2]);            // Imprime: "Otoño"

Este mapeo bidireccional es exclusivo de los enums numéricos y no está disponible en otros tipos de enums.

Enums en tiempo de compilación

Los enums numéricos generan código en tiempo de ejecución, lo que significa que existen como objetos reales en JavaScript después de la compilación:

// Código TypeScript
enum Colores {
  Rojo,
  Verde,
  Azul
}

// Se compila aproximadamente a este JavaScript
var Colores;
(function (Colores) {
    Colores[Colores["Rojo"] = 0] = "Rojo";
    Colores[Colores["Verde"] = 1] = "Verde";
    Colores[Colores["Azul"] = 2] = "Azul";
})(Colores || (Colores = {}));

Este comportamiento permite el mapeo bidireccional mencionado anteriormente, pero también significa que los enums numéricos tienen una presencia en el código final.

Uso práctico en aplicaciones

Los enums numéricos son especialmente útiles para representar conjuntos fijos de opciones relacionadas:

enum EstadoPedido {
  Pendiente = 1,
  Procesando,
  Enviado,
  Entregado,
  Cancelado
}

class Pedido {
  id: number;
  estado: EstadoPedido;
  
  constructor(id: number) {
    this.id = id;
    this.estado = EstadoPedido.Pendiente;
  }
  
  actualizarEstado(nuevoEstado: EstadoPedido): void {
    // Validación de transiciones de estado
    if (this.estado === EstadoPedido.Cancelado) {
      console.error("No se puede actualizar un pedido cancelado");
      return;
    }
    
    if (nuevoEstado === EstadoPedido.Entregado && this.estado !== EstadoPedido.Enviado) {
      console.error("Un pedido debe estar enviado antes de ser entregado");
      return;
    }
    
    this.estado = nuevoEstado;
    console.log(`Pedido #${this.id} actualizado a: ${EstadoPedido[nuevoEstado]}`);
  }
}

const miPedido = new Pedido(12345);
console.log(`Estado inicial: ${EstadoPedido[miPedido.estado]}`);  // "Pendiente"

miPedido.actualizarEstado(EstadoPedido.Procesando);
miPedido.actualizarEstado(EstadoPedido.Enviado);
miPedido.actualizarEstado(EstadoPedido.Entregado);

En este ejemplo, utilizamos un enum para representar los posibles estados de un pedido, lo que hace que el código sea más legible y mantenible que si usáramos simples números.

¿Te está gustando esta lección?

Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.

Progreso guardado
Asistente IA
Ejercicios
Iniciar sesión gratis

Más de 25.000 desarrolladores ya confían en CertiDevs

Enums de string

Los enums de string en TypeScript proporcionan una alternativa a los enums numéricos, permitiendo asignar valores de texto en lugar de números a cada miembro del enum. Esta característica resulta especialmente útil cuando necesitamos valores más descriptivos o cuando integramos con APIs o sistemas que esperan cadenas de texto específicas.

Para crear un enum de string, simplemente asignamos valores de texto a cada miembro del enum:

enum Idioma {
  Español = "es",
  Inglés = "en",
  Francés = "fr",
  Alemán = "de",
  Japonés = "ja"
}

// Uso del enum
const idiomaSeleccionado: Idioma = Idioma.Español;
console.log(idiomaSeleccionado);  // Imprime: "es"

A diferencia de los enums numéricos, los enums de string requieren que todos los miembros tengan un inicializador explícito. No es posible la asignación automática de valores como ocurre con los enums numéricos.

Ventajas de los enums de string

Los enums de string ofrecen varias ventajas significativas en ciertos escenarios:

enum EventoUsuario {
  Registro = "USUARIO_REGISTRADO",
  InicioSesion = "INICIO_SESION",
  CierreSesion = "CIERRE_SESION",
  CambioContraseña = "CAMBIO_CONTRASEÑA"
}

function registrarEvento(evento: EventoUsuario, datos: any): void {
  console.log(`Evento: ${evento}, Datos:`, datos);
  // Enviar a sistema de análisis o registro
}

registrarEvento(EventoUsuario.Registro, { userId: 123, fecha: new Date() });
// Imprime: "Evento: USUARIO_REGISTRADO, Datos: { userId: 123, fecha: [Date] }"

En este ejemplo, los valores de string son más descriptivos y autoexplicativos cuando se registran o depuran, lo que facilita el seguimiento de eventos en logs o sistemas de monitoreo.

Uso con APIs y serialización

Los enums de string son ideales para trabajar con APIs externas que esperan valores específicos:

enum TipoContenido {
  JSON = "application/json",
  XML = "application/xml",
  FormData = "multipart/form-data",
  TextoPlano = "text/plain"
}

function realizarPeticion(url: string, tipo: TipoContenido, datos: any): void {
  console.log(`Enviando petición a ${url}`);
  console.log(`Content-Type: ${tipo}`);
  console.log(`Datos:`, datos);
  
  // Código para realizar la petición HTTP
  // fetch(url, { 
  //   headers: { 'Content-Type': tipo },
  //   body: JSON.stringify(datos)
  // });
}

realizarPeticion(
  "https://api.ejemplo.com/datos",
  TipoContenido.JSON,
  { nombre: "Juan", edad: 30 }
);

Este enfoque proporciona seguridad de tipos y autocompletado mientras garantiza que los valores enviados sean exactamente los esperados por la API.

Enums de string en tiempo de compilación

A diferencia de los enums numéricos, los enums de string no admiten mapeo bidireccional. Esto significa que no podemos obtener el nombre del miembro a partir de su valor:

enum Color {
  Rojo = "RED",
  Verde = "GREEN",
  Azul = "BLUE"
}

console.log(Color.Rojo);       // Imprime: "RED"
console.log(Color["Rojo"]);    // Imprime: "RED"
console.log(Color["RED"]);     // Error: La propiedad 'RED' no existe en el tipo 'typeof Color'

Esta limitación se debe a que TypeScript solo genera un mapeo unidireccional para los enums de string en el código JavaScript resultante:

// Código TypeScript
enum Formato {
  PDF = "pdf",
  DOCX = "docx",
  XLSX = "xlsx"
}

// Se compila aproximadamente a este JavaScript
var Formato;
(function (Formato) {
    Formato["PDF"] = "pdf";
    Formato["DOCX"] = "docx";
    Formato["XLSX"] = "xlsx";
})(Formato || (Formato = {}));

Enums de string heterogéneos

También es posible crear enums heterogéneos que combinen valores de string y numéricos, aunque esta práctica no es muy común y puede generar confusión:

enum Configuracion {
  Tema = "oscuro",
  VersionAPI = 2,
  MaxElementos = 100,
  Idioma = "es"
}

console.log(Configuracion.Tema);        // "oscuro"
console.log(Configuracion.VersionAPI);  // 2

En general, es recomendable mantener la consistencia utilizando un solo tipo de valor dentro de un enum.

Enums de string vs objetos literales

Con la introducción de as const en TypeScript, existe una alternativa a los enums de string utilizando objetos literales:

// Usando enum de string
enum Rol {
  Admin = "ADMIN",
  Usuario = "USUARIO",
  Invitado = "INVITADO"
}

// Alternativa con objeto literal y 'as const'
const RolObj = {
  Admin: "ADMIN",
  Usuario: "USUARIO",
  Invitado: "INVITADO"
} as const;

type RolType = typeof RolObj[keyof typeof RolObj];

// Uso con enum
function tienePermiso(rol: Rol): boolean {
  return rol === Rol.Admin;
}

// Uso con objeto literal
function tienePermisoObj(rol: RolType): boolean {
  return rol === RolObj.Admin;
}

Ambos enfoques proporcionan seguridad de tipos, pero los enums ofrecen una sintaxis más concisa y están diseñados específicamente para este propósito.

Enums const

Además de los enums numéricos y de cadena que ya vimos, TypeScript ofrece una variante optimizada llamada enums const.

Piensa en ellos como enums normales, pero con una diferencia importante orientada a la eficiencia: no generan código JavaScript adicional en tiempo de ejecución. En lugar de crear un objeto en el código compilado, TypeScript reemplaza cada uso de un miembro del enum const directamente por su valor correspondiente durante la compilación.

Para declarar un enum const, simplemente se añade la palabra clave const antes de enum:

const enum DiaSemana {
  Lunes,
  Martes,
  Miercoles,
  Jueves,
  Viernes,
  Sabado,
  Domingo
}

const hoy: DiaSemana = DiaSemana.Miercoles;
console.log(hoy); // Imprime: 2

A primera vista, este código parece idéntico a un enum numérico normal, pero la diferencia fundamental está en lo que ocurre durante la compilación.

Diferencias en tiempo de compilación

La principal ventaja de los enums const es su comportamiento durante la compilación. Mientras que los enums regulares generan objetos JavaScript completos, los enums const se sustituyen directamente por sus valores literales en el código compilado:

// Código TypeScript con enum regular
enum Color {
  Rojo,
  Verde,
  Azul
}
const miColor = Color.Verde;

// JavaScript generado (algo parecido a esto, crea un objeto Color)
var Color;
(function (Color) {
    Color[Color["Rojo"] = 0] = "Rojo";
    Color[Color["Verde"] = 1] = "Verde";
    Color[Color["Azul"] = 2] = "Azul";
})(Color || (Color = {}));
const miColor = Color.Verde;

// Código TypeScript con enum const
const enum ColorConst {
  Rojo,
  Verde,
  Azul
}
const miColorConst = ColorConst.Verde;

// JavaScript generado (mucho más simple, Solo el valor literal)
const miColorConst = 1;

Como puedes ver en el ejemplo de JavaScript generado, el enum const no genera estructura extra; solo sustituye el nombre (ColorConst.Verde) por el valor (1). Esto resulta en un código JavaScript compilado más simple y ligero.

Casos de uso ideales

Los enums const son muy útiles cuando defines constantes con nombre que se usarán directamente en tu código, y donde la prioridad es tener un código JavaScript final lo más pequeño posible.

const enum TamañoImagen {
  Pequeño = 200,
  Mediano = 500,
  Grande = 800,
  ExtraGrande = 1200
}

function redimensionarImagen(url: string, tamaño: TamañoImagen): string {
  return `${url}?width=${tamaño}`;
}

// Cuando compilas, la llamada a la función se convierte internamente en algo como:
// return `${url}?width=${800}`;
const urlImagen = redimensionarImagen("https://ejemplo.com/foto.jpg", TamañoImagen.Grande);

Este enfoque es perfecto para constantes relacionadas que se usan con frecuencia porque el compilador simplemente inserta el valor numérico o de cadena en el lugar donde se usa el miembro del enum.

Limitaciones importantes

Es crucial saber que los enums const tienen algunas limitaciones debido a que no existen como un objeto en tiempo de ejecución:

const enum API {
  Usuarios = "api/usuarios",
  Productos = "api/productos",
  Pedidos = "api/pedidos"
}

// Esto funciona bien (acceso directo por nombre)
fetch(API.Usuarios);

// Esto NO es posible con enums const (acceso usando una variable o expresión)
const nombreEndpoint = "Usuarios";
// return API[nombreEndpoint]; // Esto daría un error en TypeScript

/*
  // Error típico (conceptual):
  // Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'typeof API'.
*/

// La principal limitación: No puedes acceder a los miembros usando una expresión dinámica
// (como una variable entre corchetes `[]`).
// Solo puedes usar el acceso directo con punto (`.`).

La limitación clave es que no puedes acceder a los miembros de un enum **const** mediante indexación dinámica (usando **[variable]**). Solo puedes acceder a ellos usando la sintaxis de punto (.) con el nombre literal del miembro. Si necesitas acceso dinámico, usa un enum regular.

Compatibilidad con módulos

Cuando usas enums const en archivos separados (módulos) y compilas tu proyecto, a veces (dependiendo de tu configuración o herramientas) TypeScript podría necesitar ayuda para manejarlos correctamente.

// archivo: enums.ts
export const enum Prioridad {
  Baja,
  Media,
  Alta,
  Critica
}

// archivo: app.ts
// import { Prioridad } from './enums'; // La importación es normal

function procesarTarea(id: number, prioridad: Prioridad) {
  console.log(`Procesando tarea ${id} con prioridad ${prioridad}`);
}

procesarTarea(123, Prioridad.Alta); // Uso normal

En ciertos escenarios donde se usa la opción de compilación isolatedModules o herramientas como Babel que compilan archivo por archivo sin ver el contexto global, los enums const exportados pueden causar problemas si no se configuran adecuadamente.

Para evitar estos posibles problemas en configuraciones avanzadas, la opción de compilación preserveConstEnums en tu tsconfig.json puede ayudar, manteniendo la estructura del enum en el código generado (además de sustituir los valores), aunque esto añade un poco del código que originalmente queríamos evitar. Este es un detalle más técnico por si te encuentras con él más adelante.

// tsconfig.json
{
  "compilerOptions": {
    "preserveConstEnums": true
  }
}

Enums const con valores de string

Los enums const funcionan perfectamente también con valores de cadena. La lógica de optimización es la misma: el compilador reemplaza el nombre del miembro por el valor literal de cadena correspondiente en el código JavaScript final.

const enum ErrorCode {
  NotFound = "NOT_FOUND",
  Unauthorized = "UNAUTHORIZED",
  BadRequest = "BAD_REQUEST"
}

function handleError(code: ErrorCode): void {
  switch (code) {
    case ErrorCode.NotFound: // En el JS compilado, esto es `case "NOT_FOUND":`
      console.log("Recurso no encontrado");
      break;
    case ErrorCode.Unauthorized: // En el JS compilado, esto es `case "UNAUTHORIZED":`
      console.log("No autorizado");
      break;
    case ErrorCode.BadRequest: // En el JS compilado, esto es `case "BAD_REQUEST":`
      console.log("Solicitud incorrecta");
      break;
  }
}

handleError(ErrorCode.NotFound); // Funciona correctamente

El compilador sustituye cada referencia como ErrorCode.NotFound por el valor literal "NOT_FOUND", haciendo que el código resultante sea más directo y no dependa de un objeto enum en tiempo de ejecución.

Enums const como alternativa a las uniones de literales

Para definir un conjunto fijo de valores de cadena relacionados, los enums const pueden ser una alternativa más legible y organizada a otras formas de hacerlo en TypeScript, como las uniones de tipos literales combinadas con as const.

// Una forma alternativa (usando tipos literales)
type DireccionLiteral = 'norte' | 'sur' | 'este' | 'oeste';

// Usando enum const (más organizado conceptualmente)
const enum DireccionEnum {
  Norte = 'norte',
  Sur = 'sur',
  Este = 'este',
  Oeste = 'oeste'
}

// Puedes usar ambos para tipado y autocompletado
function mover(direccion: DireccionLiteral, pasos: number): void {
  console.log(`Moviendo ${pasos} pasos hacia ${direccion}`);
}

function moverEnum(direccion: DireccionEnum, pasos: number): void {
  console.log(`Moviendo ${pasos} pasos hacia ${direccion}`);
}

mover('norte', 5); // Funciona
moverEnum(DireccionEnum.Norte, 5); // También funciona, y agrupa los valores bajo DireccionEnum

Los enums const ofrecen la ventaja de agrupar conceptualmente estos valores bajo un nombre (DireccionEnum), lo que puede hacer que el código sea más claro en comparación con solo usar la unión de literales por sí sola.

Consideraciones de rendimiento

Los enums const son particularmente beneficiosos en aplicaciones grandes donde la reducción del tamaño del bundle es importante:

// Usando un enum const para configuración
const enum ConfiguracionApp {
  MaxUsuariosConcurrentes = 100,
  TiempoSesion = 30 * 60 * 1000, // 30 minutos en milisegundos
  UrlBase = "https://api.miapp.com/v2",
  ModoDebug = false
}

// Cada vez que uses un miembro de este enum, el compilador insertará el valor literal
// Esto evita crear y mantener un objeto 'ConfiguracionApp' en el código JavaScript final,
// lo que reduce el tamaño total del archivo.

En aplicaciones grandes con muchos enums y muchas referencias a sus miembros, el ahorro en el tamaño del archivo JavaScript compilado usando enums const en lugar de enums regulares puede ser notable. Esto contribuye a tiempos de carga más rápidos y, en general, a un código más eficiente.

Cuándo usar enums const vs enums regulares

La elección entre usar un enum const y un enum regular depende de lo que necesites:

Usa enums **const** cuando:

  • Quieres que el código JavaScript final sea lo más pequeño posible (optimización).
  • Solo necesitas usar el valor del miembro directamente (acceso por punto .) y no necesitas acceso dinámico (usando [variable]).
  • No necesitas obtener el nombre del miembro a partir de su valor (mapeo inverso).
  • No necesitas iterar sobre los miembros del enum en tiempo de ejecución.
  • Quieres evitar la creación de un objeto JavaScript adicional para el enum.

Usa enums regulares cuando:

  • Necesitas acceder a los miembros del enum mediante indexación dinámica (usando [variable]).
  • Necesitas obtener el nombre de un miembro a partir de su valor (mapeo inverso).
  • Necesitas iterar sobre los miembros del enum en tiempo de ejecución (por ejemplo, para obtener una lista de todos los posibles valores).
  • Necesitas que el enum exista como un objeto en tiempo de ejecución por cualquier otra razón.

Los enums const son una característica avanzada de optimización que proporciona beneficios de rendimiento a cambio de algunas limitaciones en su uso en tiempo de ejecución. Útiles si la optimización del tamaño del código es una prioridad y las limitaciones no afectan tu caso de uso.

Aprendizajes de esta lección

  • Comprender qué son los enums numéricos y cómo se asignan valores automáticamente o manualmente.
  • Aprender el uso de enums de string y sus ventajas frente a los enums numéricos.
  • Conocer el concepto y beneficios de los enums const para optimizar el código compilado.
  • Entender el acceso bidireccional en enums numéricos y las limitaciones en enums de string.
  • Aplicar enums en casos prácticos como estados, flags y discriminated unions para mejorar la legibilidad y seguridad de tipos.

Completa TypeScript y certifícate

Únete a nuestra plataforma y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.

Asistente IA

Resuelve dudas al instante

Ejercicios

Practica con proyectos reales

Certificados

Valida tus conocimientos

Más de 25.000 desarrolladores ya se han certificado con CertiDevs

⭐⭐⭐⭐⭐
4.9/5 valoración