¿Por qué usar enums en TypeScript?
Los enums te permiten definir conjuntos de constantes con nombre que TypeScript valida en tiempo de compilación. A diferencia de usar strings sueltos, los enums previenen typos, facilitan refactoring y autocompletan en el IDE. Son ideales para status, roles, tipos de contenido y cualquier valor que tenga opciones limitadas y predefinidas.
Un error común es usar enums numéricos por defecto (enum Direction { Up, Down }). Esto genera valores 0, 1, 2... que son poco legibles en logs y bases de datos. Preferí siempre string enums con valores explícitos: enum Direction { Up = 'up', Down = 'down' }. Esto hace tu código más debuggeable y compatible con APIs REST.
Otro beneficio: TypeScript permite usar enums como tipos. Si definís type Status = UserStatus.ACTIVE | UserStatus.PENDING, el compilador valida que solo uses esos dos valores específicos, dándote seguridad de tipos granular sin repetir strings.
Convenciones de nomenclatura para enums
El nombre del enum debe ser singular y en PascalCase: UserRole, OrderStatus, PaymentMethod. Los valores dentro van en SCREAMING_SNAKE_CASE (convención mayoritaria) o camelCase según tu equipo, pero mantené consistencia. El string value debe ser lowercase con guiones bajos: PENDING = 'pending', IN_PROGRESS = 'in_progress'.
Evitá prefijos redundantes. Si tu enum se llama UserStatus, no hace falta que los valores sean USER_ACTIVE, USER_PENDING. Con Status.ACTIVE ya queda claro en el contexto. Esta convención mantiene el código limpio y reduce ruido visual.
Para APIs públicas o configuraciones persistidas, los string values son parte de tu contrato. Una vez que ACTIVE = 'active' está en producción, cambiar ese string rompe integraciones. Por eso elegí valores descriptivos y estables desde el principio, no códigos crípticos como A, P, C que vas a lamentar después.
Cuándo usar const enum vs enum regular
Los const enums (const enum Color { Red = 'red' }) se eliminan completamente en la compilación: TypeScript reemplaza Color.Red por 'red' inline. Esto reduce el tamaño del bundle pero perdés la posibilidad de iterar sobre el enum en runtime o de hacer reverse mapping.
Usá const enum solo para valores internos de tu app donde sabés que no necesitás reflection ni iteración. Para exports de librerías, enums de configuración que necesitás validar dinámicamente o casos donde querés Object.values(Status), usá enums regulares.
Un caso práctico: si necesitás generar un select con todas las opciones de un enum, necesitás iterar: Object.values(PaymentMethod).map(method => option). Eso no funciona con const enum. En cambio, para performance crítica en código interno (ej: un game loop con miles de comparaciones por segundo), const enum puede ahorrar microsegundos.
Integrando enums con validadores y APIs
Los enums TypeScript no existen en runtime JavaScript, así que necesitás validación explícita en boundaries (APIs, forms, DB). Con Zod podés hacer: z.nativeEnum(UserStatus) o z.enum(['active', 'pending', ...]). Esto valida que el string recibido sea un valor legal del enum.
Para bases de datos, almacená el string value, no el índice numérico. Si tu enum evoluciona (agregás un valor en el medio), los índices cambian y corrompen data existente. Con strings, podés agregar Status.ARCHIVED sin romper registros que tienen 'active' o 'pending'.
Al serializar a JSON, los enums string se serializan directamente como strings, lo cual es perfecto. Si necesitás documentar una API (OpenAPI/Swagger), los enums aparecen como campos con valores permitidos, generando validación automática y mejor documentación para consumidores de la API.