Hvordan sammenligne enums i TypeScript
I TypeScript vil jeg sammenligne to variabler som inneholder enum-verdier. Her er mitt minimale kodeeksempel:
enum E {
A,
B
}
let e1: E = E.A
let e2: E = E.B
if (e1 === e2) {
console.log("equal")
}
Når jeg kompilerer med tsc (v 2.0.3) får jeg følgende feil:
TS2365: Operator '===' kan ikke brukes på typene 'E.A' og 'E.B'.
Det samme gjelder ==, !== og !=. Jeg prøvde å legge til const-nøkkelordet, men det ser ut til å ikke ha noen effekt. TypeScript-spesifikasjonen sier følgende:
4.19.3 Operatorene <, >, <=, >=, ==, !=, ===, og !==
Disse operatorene krever at en eller begge operandtypene kan tildeles den andre. Resultatet er alltid av den boolske primitivtypen.
Noe som (jeg tror) forklarer feilen. Men hvordan kan jeg komme rundt det?
Side notis
Jeg bruker Atom-editoren med atom-typescript, og jeg får ingen feil/advarsler i editoren min. Men når jeg kjører tsc i samme katalog, får jeg feilen nevnt ovenfor. Jeg trodde de skulle bruke den samme tsconfig.json-filen, men åpenbart er det ikke tilfelle.
Svar #1
Vel, jeg tror jeg har funnet noe som fungerer:
if (e1.valueOf() === e2.valueOf()) {
console.log("equal")
}
Men jeg er litt overrasket over at dette ikke er nevnt noe sted i dokumentasjonen.
Svar #2
Det finnes en annen måte: hvis du ikke vil at generert JavaScript-kode skal påvirkes på noen måte, kan du bruke typekonvertering:
let e1: E = E.A
let e2: E = E.B
if (e1 as E === e2 as E) {
console.log("equal")
}
Generelt skyldes dette kontrollflytbasert typeinfernce. Med nåværende TypeScript-implementering er det slått av hver gang en funksjonskall er involvert, så du kan også gjøre dette:
let id = a => a
let e1: E = id(E.A)
let e2: E = id(E.B)
if (e1 === e2) {
console.log('equal');
}
Det merkelige er at det fortsatt ikke finnes noen feil hvis id-funksjonen erklæres å returnere nøyaktig samme type som argumentet:
function id<T>(t: T): T { return t; }
Svar #3
Jeg ville definere verdier for Enum på denne måten og sammenligne med ===
const enum AnimalInfo {
Tiger = "Tiger",
Lion = "Lion"
}
let tigerStr = "Tiger";
if (tigerStr === AnimalInfo.Tiger) {
console.log('true');
} else {
console.log('false');
}
Svar #4
Hvis jeg kunne sammenligne to enum med dette
if (product.ProductType &&
(product.ProductType.toString() == ProductTypes[ProductTypes.Merchandises])) {
// yes this item is of merchandises
}
med ProductTypes være denne export enum ProductTypes{Merchandises,Goods,...}
Svar #5
Original AUG/18
I mitt tilfelle fungerte ingen av de ovennevnte løsningene, grunnen var at jeg kastet enum-verdien til enum-objektet.
Etter det prøvde jeg å finne ut om enumen var ekvivalent med et annet enum-objekt... så jeg har laget følgende generiske funksjoner:
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;
}
Dette er et eksempel på min testtilfelle:
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
}
Den koden virket ikke, etter at jeg har prøvd løsningene som ble gitt her i dette spørsmålet, har jeg funnet ut at når enumRefKey er gyldig, skrev console.log(parsedEnum) ut tall og teksten VALUE_DEF når den ikke er. Samme resultat skjedde med alle de andre løsningene:
- parsedEnum som SomeEnum
- parsedEnum.valueOf()
- SomeEnum[parsedEnum]
Løsningen ved bruk av de generiske metodene ser slik ut:
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
}
Oppdater SEP/21
Den beste måten å unngå alle problemer knyttet til enums i TypeScript-sammenligning er å erklære dem som i følgende eksempel.
I stedet for dette:
enum SomeEnum {
VALUE1, VALUE2, VALUE3
}
Gjør dette:
enum SomeEnum {
VALUE1 = 'VALUE1', VALUE2 = 'VALUE2', VALUE3 = 'VALUE3'
}
På denne måten fra nå av vil du ikke trenge å kaste eller konvertere enum-verdier til enum-objekter, og hvis du trenger det, vil det alltid fungere. Med denne løsningen er alle følgende eksempler gyldige, og de vil returnere 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'
Gjerningsperson
Grunnen til alle disse problemene er at når TypeScript kompileres til JavaScript, blir enums analysert som objekter på denne måten
// 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'
}
Som du kan se, når enumen er analysert til JS, blir årsaken til alle sammenligningsproblemene åpenbar, da vi ved en feiltakelse kan sammenligne string mot number, noe som kan føre til falske positive resultater. Følgende er den andre enumen analysert til JS som fungerer mye bedre:
// this enum at TS
enum SomeEnum {
VALUE1 = 'VALUE1', VALUE2 = 'VALUE2', VALUE3 = 'VALUE3'
}
// is parsed to JS like this:
{
'VALUE1': 'VALUE1', 'VALUE2': 'VALUE2', 'VALUE3': 'VALUE3'
}
