Enums vergelijken in TypeScript

In TypeScript wil ik twee variabelen vergelijken die enumwaarden bevatten. Hier is mijn minimale codevoorbeeld:

enum E {
  A,
  B
}
let e1: E = E.A
let e2: E = E.B
if (e1 === e2) {
  console.log("equal")
}

Bij het compileren met tsc(v 2.0.3) krijg ik de volgende foutmelding:

TS2365: Operator ‘===’ kan niet worden toegepast op typen ‘E.A’ en ‘E.B’.

Hetzelfde met ==, !==en !=.
Ik heb geprobeerd het trefwoord consttoe te voegen, maar dat lijkt geen effect te hebben.
De TypeScript-specificatiezegt het volgende:

4.19.3 De operatoren <, >, <=, >=, ==, !=, === en !==

Voor deze operatoren moet een of beide operandtypen aan de andere kunnen worden toegewezen. Het resultaat is altijd van het Booleaanse primitieve type.

Wat (denk ik) de fout verklaart. Maar hoe kan ik er omheen?

Kanttekening
Ik gebruik de Atom-editor met atom-typescripten ik krijg geen fouten/ waarschuwingen in mijn editor. Maar wanneer ik tscin dezelfde map uitvoer, krijg ik de bovenstaande foutmelding. Ik dacht dat ze hetzelfde tsconfig.json-bestand zouden gebruiken, maar blijkbaar is dat niet het geval.


Antwoord 1, autoriteit 100%

Nou, ik denk dat ik iets heb gevonden dat werkt:

if (e1.valueOf() === e2.valueOf()) {
  console.log("equal")
}

Maar het verbaast me een beetje dat dit nergens in de documentatie wordt vermeld.


Antwoord 2, autoriteit 60%

Er is een andere manier: als u niet wilt dat de gegenereerde javascript-code op enigerlei wijze wordt beïnvloed, kunt u type cast gebruiken:

let e1: E = E.A
let e2: E = E.B
if (e1 as E === e2 as E) {
  console.log("equal")
}

Over het algemeen wordt dit veroorzaakt door op control-flow gebaseerde type-inferentie. Met de huidige typoscript-implementatie is het uitgeschakeld wanneer er een functieaanroep bij betrokken is, dus u kunt dit ook doen:

let id = a => a
let e1: E = id(E.A)
let e2: E = id(E.B)
if (e1 === e2) {
  console.log('equal');
}

Het rare is dat er nog steeds geen fout is als de functie idwordt gedeclareerd om precies hetzelfde type terug te geven als zijn agument:

function id<T>(t: T): T { return t; }

Antwoord 3, autoriteit 26%

Als ik hiermee twee opsommingen kon vergelijken

if (product.ProductType && 
       (product.ProductType.toString() == ProductTypes[ProductTypes.Merchandises])) {
      // yes this item is of merchandises
  } 

waarbij ProductTypes deze export enum ProductTypes{Merchandises,Goods,...}


Antwoord 4, autoriteit 23%

Ik zou waarden voor Enum op deze manier definiëren en vergelijken met ===

const enum AnimalInfo {
Tiger = "Tiger",
Lion = "Lion"
}
let tigerStr = "Tiger";
if (tigerStr === AnimalInfo.Tiger) {
  console.log('true');
} else {
  console.log('false');
}

Antwoord 5, autoriteit 11%

Het enige dat voor mij werkte (in typoscript 2.2.1) was dit:

if (E[e1] === E[e2]) {
  console.log("equal")
}

Dit vergelijkt de tekenreeksen die de namen vertegenwoordigen (bijv. “A” en “B”).


Antwoord 6, autoriteit 6%

Het typen van opsommingen naar strings is een zeer waardevolle techniek.

Bijvoorbeeld;

if (String(e1) === String(e2)) {
    console.log("equal, now actually works!")
}

Antwoord 7, autoriteit 4%

In typoscript een voorbeeld-enum:

enum Example {
   type1,
   type2
};

wordt omgezet naar javascript in dit object:

Example {
    '0': 'type1', 'type1': 0,
    '1': 'type2', 'type2': 1
}

Ik had veel problemen met vergelijkende opsommingen in typoscript. Dit eenvoudige script lost het probleem op:

enum Example {
    type1 = 'type1',
    type2 = 'type2'
};

vervolgens wordt het object in javascript omgezet in:

Example {
    'type1': 'type1',
    'type2': 'type2'
}

Als u geen opsommingen hoeft te gebruiken, kunt u deze beter niet gebruiken. Typescript heeft meer geavanceerde typen, meer hier:
https://www.typescriptlang.org/docs/handbook/advanced-types. html
U kunt in plaats daarvan gebruiken:

type Example = 'type1' | 'type2';

Antwoord 8, autoriteit 2%

De fout wordt gegenereerd omdat de compiler zich realiseert dat de instructie altijd onwaar is en daarom overbodig. Je declareert twee variabelen die duidelijk niet gelijk zijn en probeert dan te zien of ze gelijk zijn.

Als je het bijvoorbeeld verandert in:

enum E {
  A,
  B
}
foo() {
  let e1: E = E.A
  let e2: E
  e2 = bar();
  if (e1 === e2) {
    console.log("equal")
  }
}
bar(): E {
  return E.B
}

het zou zonder fouten moeten compileren.

Een kanttekening, sth. leuk vinden

let e1 = E.A;
if (e1 && e1 === E.B) {
  ...
}

zou ook niet compileren, aangezien e1in dit geval 0is (aangezien A de eerste enum ‘optie’ is) en daarom falsewat betekent dat de tweede toestand nooit zou worden bereikt (ongeacht of de tweede verklaring in dit geval zelfs geldig zou zijn)


Antwoord 9, autoriteit 2%

Origineel AUG/18

In mijn geval werkte geen van de bovenstaande oplossingen, de reden was dat ik de enum-waarde naar het enum-object aan het casten was.

Daarna probeerde ik te weten of de enum equivalent was aan een ander enum-object… dus heb ik de volgende algemenefuncties gemaakt:

 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;
    }
    while (typeof v !== validType) {
      v = enumType[v];
    }
    return v;
  }

Dit is een voorbeeld van mijn testcase:

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
}

Het is duidelijk dat die code niet werkte, nadat ik de oplossingen heb geprobeerd die hier bij deze vragen worden gegeven, heb ik ontdekt dat wanneer enumRefKey geldig is console.log(parsedEnum)nummers aan het afdrukken was en de tekst VALUE_DEF wanneer niet. Hetzelfde resultaat gebeurde met alle andere oplossingen:

  • parsedEnum als SomeEnum
  • parsedEnum.valueOf()
  • SomeEnum[parsedEnum]

De oplossing met behulp van de generieke methoden ziet er als volgt uit:

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
}

Update SEP/21

De beste manier om alle problemen met betrekking tot enumsin TypeScript-vergelijking te vermijden, is door ze te declareren zoals in het volgende voorbeeld.

In plaats van dit:

enum SomeEnum {
  VALUE1, VALUE2, VALUE3
}

Doe dit:

enum SomeEnum {
  VALUE1 = 'VALUE1', VALUE2 = 'VALUE2', VALUE3 = 'VALUE3'
}

Op deze manier hoef je vanaf nu geen enum-waarden te casten of converteren naar enum-objecten, en als het nodig is, werkt het altijd. Met deze oplossing zijn alle volgende voorbeelden geldig en retourneren ze 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'

Dader

De reden voor al deze problemen is dat wanneer TypeScriptwordt gecompileerd naar JavaScriptenums worden geparseerd als objectszoals deze

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

Zoals je kunt zien, wordt de reden voor alle vergelijkingsproblemen, zodra de opsomming eenmaal is geparseerd naar JS, duidelijk, omdat we per abuis stringversus numberkunnen vergelijken, wat eindigen in vals-positieve resultaten. Het volgende is de tweede opsomming van JS die veel beter werkt:

// this enum at TS
enum SomeEnum {
  VALUE1 = 'VALUE1', VALUE2 = 'VALUE2', VALUE3 = 'VALUE3'
}
// is parsed to JS like this:
{
  'VALUE1': 'VALUE1', 'VALUE2': 'VALUE2', 'VALUE3': 'VALUE3'
} 

Other episodes