Como comparar Enums em TypeScript
No TypeScript, quero comparar duas variáveis que contêm valores de enum. Aqui está meu exemplo de código mínimo:
enum E {
A,
B
}
let e1: E = E.A
let e2: E = E.B
if (e1 === e2) {
console.log("equal")
}
Ao compilar com tsc (v 2.0.3) obtenho o seguinte erro:
TS2365: O operador '===' não pode ser aplicado aos tipos 'E.A' e 'E.B'.
O mesmo com ==, !== e !=. Tentei adicionar a palavra-chave const, mas isso parece não ter efeito. A especificação do TypeScript diz o seguinte:
4.19.3 Os operadores <, >, <=, >=, ==, !=, === e !==
Esses operadores exigem que um ou ambos os tipos de operando possam ser atribuídos ao outro. O resultado é sempre do tipo primitivo Boolean.
O que (eu acho) explica o erro. Mas como posso contorná-lo?
Observação
Estou usando o editor Atom com o atom-typescript, e não recebo nenhum erro/aviso no meu editor. Mas quando executo tsc no mesmo diretório, recebo o erro acima. Eu pensei que eles deveriam usar o mesmo arquivo tsconfig.json, mas aparentemente não é o caso.
Resposta #1
Bem, acho que encontrei algo que funciona:
if (e1.valueOf() === e2.valueOf()) {
console.log("equal")
}
Mas estou um pouco surpreso que isso não seja mencionado em nenhum lugar da documentação.
Resposta #2
Há outra maneira: se você não quiser que o código JavaScript gerado seja afetado de nenhuma forma, você pode usar conversão de tipo:
let e1: E = E.A
let e2: E = E.B
if (e1 as E === e2 as E) {
console.log("equal")
}
Em geral, isso é causado por inferência de tipo baseada em fluxo de controle. Com a implementação atual do TypeScript, isso é desativado sempre que uma chamada de função está envolvida, então você também pode fazer isso:
let id = a => a
let e1: E = id(E.A)
let e2: E = id(E.B)
if (e1 === e2) {
console.log('equal');
}
O estranho é que ainda não há erro se a função id for declarada para retornar exatamente o mesmo tipo que seu argumento:
function id<T>(t: T): T { return t; }
Resposta #3
Eu definiria valores para Enum assim e compararia com ===
const enum AnimalInfo {
Tiger = "Tiger",
Lion = "Lion"
}
let tigerStr = "Tiger";
if (tigerStr === AnimalInfo.Tiger) {
console.log('true');
} else {
console.log('false');
}
Resposta #4
Se pudesse comparar dois enums com isso
if (product.ProductType &&
(product.ProductType.toString() == ProductTypes[ProductTypes.Merchandises])) {
// yes this item is of merchandises
}
com ProductTypes sendo este export enum ProductTypes{Merchandises,Goods,...}
Resposta #5
Original 18/AGO
No meu caso, nenhuma das soluções acima funcionou, a razão foi que eu estava convertendo o valor do enum para o objeto enum.
Depois disso, eu estava tentando saber se o enum era equivalente a outro objeto enum... então eu criei as seguintes funções genéricas:
public static enumEquals<T>(e: any, e1: T, e2: T): boolean {
const v1 = this.enumValue(e, e1);
return v1 === this.enumValue(e, e2, typeof v1);
}
private static enumValue<T>(enumType: any, value: T, validType?: string) {
let v = enumType[value];
if (!validType) {
return v;
}
if (typeof v !== validType) {
v = enumType[v];
}
if (typeof v !== validType) {
v = enumType[v];
}
return v || null;
}
Este é um exemplo do meu caso de teste:
enum SomeEnum {
VALUE1, VALUE2, VALUE3, VALUE_DEF
}
const enumRefKey = localStorage.getItem('someKey');
const parsedEnum = SomeEnum[enumRefKey] || SomeEnum.VALUE_DEF;
console.log(parsedEnum);
if (parsedEnum === SomeEnum.VALUE_DEF) {
// do stuff
}
Esse código não funcionou, depois de eu ter tentado as soluções dadas aqui nesta pergunta, descobri que quando enumRefKey é válido console.log(parsedEnum) estava imprimindo números e o texto VALUE_DEF quando não é. O mesmo resultado aconteceu usando todas as outras soluções:
- parsedEnum como SomeEnum
- parsedEnum.valueOf()
- SomeEnum[parsedEnum]
A solução usando os métodos genéricos se parece com isto:
enum SomeEnum {
VALUE1, VALUE2, VALUE3, VALUE_DEF
}
const enumRefKey = localStorage.getItem('someKey');
const parsedEnum = SomeEnum[enumRefKey] || SomeEnum.VALUE_DEF;
console.log(parsedEnum);
if (this.enumEquals(SomeEnum, parsedEnum, SomeEnum.VALUE_DEF) {
// do stuff
}
Atualizar SET/21
A melhor maneira de evitar todos os problemas relacionados a enums na comparação de TypeScript é declará-los como no seguinte exemplo.
Em vez disso:
enum SomeEnum {
VALUE1, VALUE2, VALUE3
}
Faça isto:
enum SomeEnum {
VALUE1 = 'VALUE1', VALUE2 = 'VALUE2', VALUE3 = 'VALUE3'
}
Dessa forma, a partir de agora você não precisará mais lançar nem converter valores de enum para objetos enum, e se precisar, sempre funcionará. Com esta solução, todos os exemplos a seguir são válidos, e eles retornarão true:
console.log(SomeEnum['VALUE1'] === 'VALUE1'); // prints 'true'
console.log(SomeEnum['VALUE1'] === SomeEnum.VALUE1); // prints 'true'
console.log(SomeEnum['VALUE1'] === 'VALUE1' as SomeEnum); // prints 'true'
console.log(SomeEnum['VALUE1'] === 'VALUE1'); // prints 'true'
console.log(SomeEnum['VALUE1'] === (<SomeEnum>'VALUE1')); // prints 'true'
console.log(SomeEnum.VALUE1 === 'VALUE1' as SomeEnum); // prints 'true'
console.log(SomeEnum.VALUE1 === (<SomeEnum>'VALUE1')); // prints 'true'
console.log(SomeEnum.VALUE1 === 'VALUE1'); // prints 'true'
Culpado
A razão para todos esses problemas é que quando TypeScript é compilado para JavaScript, os enums são analisados como objetos assim
// this enum at TS
enum SomeEnum {
VALUE1, VALUE2, VALUE3
}
// is parsed to JS like this:
{
VALUE1: 1, VALUE2: 2, VALUE3: 3, 1: 'VALUE1', 2: 'VALUE2', 3: 'VALUE3'
}
Como você pode ver, uma vez que o enum é analisado para JS, a razão para todos os problemas de comparação se torna óbvia, pois podemos estar comparando erroneamente string com number, o que pode resultar em falsos positivos. O seguinte é o segundo enum analisado para JS, que funciona muito melhor:
// this enum at TS
enum SomeEnum {
VALUE1 = 'VALUE1', VALUE2 = 'VALUE2', VALUE3 = 'VALUE3'
}
// is parsed to JS like this:
{
'VALUE1': 'VALUE1', 'VALUE2': 'VALUE2', 'VALUE3': 'VALUE3'
}
