Introducción: La pregunta eterna en Stack Overflow
\n\n¿Alguna vez te has preguntado cómo los desarrolladores experimentados en TypeScript obtienen los nombres de los valores enum? Si has mirado en Stack Overflow, seguramente has visto la pregunta con miles de visitas: "How to get names of enum entries?". Parecería sencillo, ¿verdad? Pero en la práctica, muchos principiantes (y no solo) tropiezan en este punto.
En este artículo no solo daremos una respuesta lista. Nos meteremos bajo el capó de TypeScript y analizaremos cómo funcionan exactamente los enum, cómo el compilador los transforma en JavaScript y por qué algunos enfoques para obtener nombres pueden provocar errores inesperados. ¿Listos? ¡Allá vamos!
¿Qué es un enum en TypeScript y para qué sirven sus nombres?
\n\nUn enum (enumeración) es una forma conveniente de definir un conjunto de constantes con nombre. En TypeScript, pueden ser numéricos (por defecto) y de cadena. Pero, ¿qué hacer si necesitas obtener el nombre de un valor específico? Por ejemplo, recibes de una API el código de estado 403 y quieres mostrar al usuario un mensaje claro como "Forbidden".
enum HttpStatus {\n OK = 200,\n NotFound = 404,\n Forbidden = 403\n}\n\n// Nuestra tarea: obtener el nombre "Forbidden" a partir del valor 403\n\n\nParece que TypeScript debería proporcionar un método incorporado para esto. Pero no. Hay que escribir nuestra propia lógica. Y aquí empieza lo interesante.
\n\nSolución directa: Object.keys y Object.values
\n\nLa forma más obvia es usar los métodos Object.keys() o Object.values(). Muchos principiantes escriben algo como esto:
function getEnumName(enumObj: any, value: number): string | undefined {\n const keys = Object.keys(enumObj);\n return keys.find(key => enumObj[key] === value);\n}\n\nconsole.log(getEnumName(HttpStatus, 403)); // "Forbidden" — ¡funciona!\n\n\nParece lógico, ¿verdad? Sí. Pero si pruebas este código con un enum numérico, te llevas una sorpresa. Veamos en qué se compila nuestro HttpStatus.
Bajo el capó: compilación de un enum numérico
\n\nCuando TypeScript compila un enum numérico, crea un objeto con mapeo inverso (reverse mapping). Así es como se ve el JavaScript compilado:
\n\nvar HttpStatus;\n(function (HttpStatus) {\n HttpStatus[HttpStatus["OK"] = 200] = "OK";\n HttpStatus[HttpStatus["NotFound"] = 404] = "NotFound";\n HttpStatus[HttpStatus["Forbidden"] = 403] = "Forbidden";\n})(HttpStatus || (HttpStatus = {}));\n\n// El objeto HttpStatus resultante se ve así:\n// { \n// "200": "OK", \n// "404": "NotFound", \n// "403": "Forbidden", \n// "OK": 200, \n// "NotFound": 404, \n// "Forbidden": 403 \n// }\n\n\n¿Ves el truco? El compilador añade tanto claves numéricas como claves de cadena. Por lo tanto, Object.keys(HttpStatus) devolverá no solo ["OK", "NotFound", "Forbidden"], sino también ["200", "404", "403"]. ¡Esto rompe la lógica de nuestra solución simple!
Para evitarlo, hay que filtrar las claves numéricas:
\n\nfunction getEnumNameSafe(enumObj: any, value: number): string | undefined {\n const keys = Object.keys(enumObj).filter(key => isNaN(Number(key)));\n return keys.find(key => enumObj[key] === value);\n}\n\n\nPero este código parece un parche. ¿Hay una forma mejor?
\n\nEnums de cadena: otra historia
\n\nSi usas enums de cadena, no se crea el mapeo inverso. Esto simplifica la tarea:
\n\nenum Direction {\n Up = "UP",\n Down = "DOWN",\n Left = "LEFT",\n Right = "RIGHT"\n}\n\n// Object.keys(Direction) devolverá solo ["Up", "Down", "Left", "Right"] — ¡ideal!\n\n\nPero, ¿qué pasa si necesitas obtener el nombre a partir del valor? El mismo Object.keys funciona perfectamente:
function getStringEn