TypeScript
Tutorial TypeScript: Enums
Aprende enums en TypeScript: tipos numéricos, string y const. Domina su uso, ventajas y optimización para código eficiente y seguro.
Aprende TypeScript y certifícateEnums 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.
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.
Otros ejercicios de programación de TypeScript
Evalúa tus conocimientos de esta lección Enums con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
Funciones
Reto composición de funciones
Reto tipos especiales
Reto tipos genéricos
Módulos
Polimorfismo
Funciones TypeScript
Interfaces
Funciones puras
Reto namespaces
Funciones flecha
Polimorfismo
Operadores
Conversor de unidades
Funciones flecha
Control de flujo
Herencia
Clases
Proyecto validación de tipado
Clases y objetos
Encapsulación
Herencia
Proyecto sistema de votación
Reto genéricos con clases
Inmutabilidad
Interfaces
Funciones de alto orden
Reto map y filter
Control de flujo
Interfaces
Reto funciones orden superior
Herencia y clases abstractas
Reto tipos mapped
Herencia de clases
Reto funciones puras
Variables y constantes
Introducción a TypeScript
Reto testing unitario
Funciones de primera clase
Clases
OOP y CRUD en TypeScript
Interfaces y su implementación
Tipos genéricos
Namespaces
Operadores y expresiones
Proyecto generador de contraseñas
Reto unión e intersección
Encapsulación
Tipos de unión e intersección
Tipos de unión e intersección
Reto hola mundo en TS
Variables y constantes
Funciones puras
Control de flujo
Introducción a TypeScript
Resolución de módulos
Control de flujo
Reto tipos de utilidad
Reto tipos literales y condicionales
Reto exportar e importar
Propiedades y métodos
Tipos de utilidad
Clases y objetos
Tipos de datos, variables y constantes
Proyecto Minigestor de tareas
Operadores
Funciones flecha y contexto
Proyecto Inventario de productos
Funciones
Reto type aliases
Funciones de alto orden
Funciones y parámetros tipados
Tipos literales
Reto enums
Tipos de utilidad
Modificadores de acceso y encapsulación
Polimorfismo
Tipos genéricos
Reto módulos
Tipos literales
Inmutabilidad
Proyecto Generator de datos
Variables y constantes
Funciones de primera clase
Todas las lecciones de TypeScript
Accede a todas las lecciones de TypeScript y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Typescript
Introducción Y Entorno
Instalación Y Configuración De Typescript
Introducción Y Entorno
Tipos De Datos, Variables Y Constantes
Sintaxis
Operadores Y Expresiones
Sintaxis
Control De Flujo
Sintaxis
Funciones Y Parámetros Tipados
Sintaxis
Funciones Flecha Y Contexto
Sintaxis
Enums
Sintaxis
Type Aliases Y Aserciones De Tipo
Sintaxis
Clases Y Objetos
Programación Orientada A Objetos
Interfaces Y Su Implementación
Programación Orientada A Objetos
Modificadores De Acceso Y Encapsulación
Programación Orientada A Objetos
Herencia Y Clases Abstractas
Programación Orientada A Objetos
Polimorfismo
Programación Orientada A Objetos
Decoradores Básicos
Programación Orientada A Objetos
Propiedades Y Métodos
Programación Orientada A Objetos
Inmutabilidad
Programación Funcional
Funciones Puras Y Efectos Secundarios
Programación Funcional
Funciones De Primera Clase
Programación Funcional
Funciones De Alto Orden
Programación Funcional
Conceptos Básicos E Inmutabilidad
Programación Funcional
Funciones De Primera Clase Y Orden Superior
Programación Funcional
Composición De Funciones
Programación Funcional
Métodos Funcionales De Arrays (Map, Filter, Reduce)
Programación Funcional
Tipos Literales Y Tipos Condicionales
Tipos Intermedios Y Avanzados
Tipos Genéricos Básicos
Tipos Intermedios Y Avanzados
Tipos De Unión E Intersección
Tipos Intermedios Y Avanzados
Tipos De Utilidad (Partial, Required, Pick, Etc)
Tipos Intermedios Y Avanzados
Unknown, Never Y Tipos Especiales
Tipos Intermedios Y Avanzados
Tipos Mapped
Tipos Intermedios Y Avanzados
Genéricos Con Clases E Interfaces
Tipos Intermedios Y Avanzados
Módulos
Namespaces Y Módulos
Namespaces
Namespaces Y Módulos
Resolución De Módulos
Namespaces Y Módulos
Exportación E Importación De Módulos
Namespaces Y Módulos
Introducción A Módulos
Namespaces Y Módulos
Testing Unitario En Typescript
Testing
En esta lección
Objetivos de aprendizaje 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.