Obtener la clave del enum con la cadena de valor (mapeo inverso) en TypeScript
Tengo un enum:
export enum ApiMessages {
logged_ok = 'Logged OK',
register_ok = 'Register OK'
}
Tengo una función con el enum como parámetro:
export function responseOK(message: ApiMessages, result ?: any): ApiResponse {
return {
"status": "ok",
"code": 200,
"messageId": ApiMessages[message], <-- KO TS7015
"message": message,
"result": result
};
}
Estoy llamando a la función de esta manera:
responseOK(ApiMessages.logged_ok, {user: userRes})
Estoy intentando devolver la clave del enum y el valor de cadena del enum en la respuesta, pero obtengo el error de TS:
TS7015: El elemento tiene implícitamente un tipo 'any' porque la expresión del índice no es de tipo 'number'.
Tengo una configuración estricta de TypeScript. Agregar suppressImplicitAnyIndexErrors no es una opción.
Versión de TypeScript: 2.9.2
Respuesta #1
Como se describe en el manual:
Ten en cuenta que los miembros del enum de tipo cadena no generan un mapeo inverso en absoluto.
Eso significa que no hay un mapeo inverso simple en tu caso.
Solución alternativa: Obtener un mapeo inverso para los miembros de un enum de cadenas
Para obtener la clave de un miembro de un enum por su valor, debes iterar a través de las claves del enum y comparar el valor asociado con tu valor objetivo.
function getEnumKeyByEnumValue(myEnum, enumValue) {
let keys = Object.keys(myEnum).filter(x => myEnum[x] == enumValue);
return keys.length > 0 ? keys[0] : null;
}
Puedes escribir esto de manera más estricta de la siguiente manera (nota que podemos interpretar nuestro enum como un tipo indexable con clave y valor siendo ambos cadenas aquí):
function getEnumKeyByEnumValue<T extends {[index:string]:string}>(myEnum:T, enumValue:string):keyof T|null {
let keys = Object.keys(myEnum).filter(x => myEnum[x] == enumValue);
return keys.length > 0 ? keys[0] : null;
}
A continuación, se muestra un código de demostración. También puedes verlo en acción en el TypeScript Playground
enum ApiMessages {
logged_ok = 'Logged OK',
register_ok = 'Register OK'
}
let exampleValue = ApiMessages.logged_ok;
let exampleKey = getEnumKeyByEnumValue(ApiMessages, exampleValue);
alert(`The value '${exampleValue}' has the key '${exampleKey}'`)
function getEnumKeyByEnumValue<T extends {[index:string]:string}>(myEnum:T, enumValue:string):keyof T|null {
let keys = Object.keys(myEnum).filter(x => myEnum[x] == enumValue);
return keys.length > 0 ? keys[0] : null;
}
Agregando esto en tu responseOK() terminas con:
function responseOK(message: ApiMessages, result ?: any) {
return {
"status": "ok",
"code": 200,
"messageId": getEnumKeyByEnumValue(ApiMessages, message),
"message": message,
"result": result
};
}
Respuesta #2
Si tienes una cadena (o cualquier otro valor) que sabes que es un valor de enumeración válido, simplemente puedes decirle al compilador que trate el valor como una enumeración.
NOTA: Esto solo funciona si sabes que tu valor es válido para el enum.
enum Fruit {
Apple = 'apple',
Bananna = 'bananna'
}
const str: string = 'apple';
const fruit: Fruit = str as Fruit;
if (fruit === Fruit.Apple)
console.log("It's an apple");
if (fruit === Fruit.Bananna)
console.log("It's a bananna");
Salida: Es una manzana
Respuesta #3
Ahora puedes obtener los rasgos y sus claves de los enums dentro de los tipos en TypeScript🎉🎉
enum ApiMessage {
logged_ok = 'Logged OK',
register_ok = 'Register OK'
}
// "logged_ok" | "register_ok"
type ApiMessageTraits = keyof typeof ApiMessage;
// "Logged OK" | "Register OK"
type MessageContents = `${ApiMessage}`;
Tristemente, esto depende de convertir las claves de los enumerables en una cadena, por lo que no puedes tener ningún otro tipo de claves excepto una cadena.
Editar: Simplifiqué enormemente el segundo método usando literales de plantilla con TypeScript >=4.1.
Editar: Ahora también puedes obtener los valores de los enums con valores numéricos mediante el nuevo análisis de cadenas de números en las cláusulas extends:
enum ApiMessage {
logged_ok = 1,
register_ok = 2,
}
// "logged_ok" | "register_ok"
type ApiMessageTraits = keyof typeof ApiMessage;
// 1 | 2
type MessageContents = `${ApiMessage}` extends `${infer N extends number}` ? N : never;
Respuesta #4
Puedes crear fácilmente un mapa que te permita obtener la clave a partir del valor sin crear una función especial para ello.
export enum ApiMessage {
logged_ok = 'Logged OK',
register_ok = 'Register OK'
}
export type ApiMessageKey = keyof typeof ApiMessage;
export const API_MESSAGE_KEYS = new Map<ApiMessage, ApiMessageKey>(
Object.entries(ApiMessage).map(([key, value]:[ApiMessageKey, ApiMessage]) => [value, key])
)
API_MESSAGE_KEYS.get(ApiMessage.logged_ok); // 'logged_ok'
