Pesquisa de site

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'
} 

Artigos relacionados