如何比较类型脚本中的枚举

在 TypeScript 中,我想比较两个包含枚举值的变量:

enum E {
A,
B
}


let e1: E = E.A
let e2: E = E.B


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

在使用 tsc(v2.0.3)编译时,我得到以下错误:

TS2365: 运算符’= = =’不能应用于‘ E.A’和‘ E.B’类型。

==!==!=也是如此。 我试图添加 const关键字,但似乎没有效果。 类型脚本规范表示:

4.19.3 < 、 > 、 < = 、 > = 、 = = 、 = = 、 ! = 、 = = = 和! = = 运算符

这些运算符要求将一个或两个操作数类型赋值给另一个操作数类型。结果始终是布尔原语类型。

这(我认为)解释了这个错误。但是我怎样才能避免这个错误呢?

边注
我在 原子打字稿中使用 Atom 编辑器,在我的编辑器中没有任何错误/警告。但是当我在同一个目录中运行 tsc时,我会得到上面的错误。我认为他们应该使用相同的 tsconfig.json文件,但显然情况并非如此。

116450 次浏览

Well I think I found something that works:

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

But I'm a bit surprised that this isn't mentioned anywhere in the documentation.

There is another way: if you don't want generated javascript code to be affected in any way, you can use type cast:

let e1: E = E.A
let e2: E = E.B




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

In general, this is caused by control-flow based type inference. With current typescript implementation, it's turned off whenever function call is involved, so you can also do this:

let id = a => a


let e1: E = id(E.A)
let e2: E = id(E.B)


if (e1 === e2) {
console.log('equal');
}

The weird thing is, there is still no error if the id function is declared to return precisely the same type as its agument:

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

The only thing that worked for me (in typescript 2.2.1) was this:

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

This compares the strings representing the names (eg. "A" and "B").

If was able to compare two enums with this

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

with ProductTypes being this export enum ProductTypes{Merchandises,Goods,...}

Original AUG/18

In my case none of the above solutions worked, the reason was that i was casting the enum value to the enum object.

After that i was trying to know if the enum was equivalent to another enum object... so i 've created the following generic functions:

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

This is an example of my test case:

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
}

Obviously that code didn't worked, after i've tried the solutions given here at this questions i've found that when enumRefKey is valid console.log(parsedEnum) was printing numbers and the text VALUE_DEF when is not. The same result happend using all other solutions:

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

The solution using the generic methods looks like this:

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

Best way to avoid all the issues related to enums in TypeScript comparison is to declare them like the following example.

Instead of this:

enum SomeEnum {
VALUE1, VALUE2, VALUE3
}

Do this:

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

This way from now you won't need to cast nor convert enum values to enum objects, and if you need to it'll always work. With this solution, all of the following examples are valid, and they'll return 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'

Culprit

The reason for all this issues is that when TypeScript is compiled to JavaScript enums are parsed as objects like this

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

As you can see, once the enum is parsed to JS the reason for all the comparison issues becomes obvious, as we mistakenly may be comparing string vs number which may end up in false-positive results. The following is the second enum parsed to JS which works way better:

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

The error is thrown because the compiler realizes that the statement is always false and therefore redundant. You declare two variables which are clearly not equal and then try and see whether they are equal.

If you change it to e.g.:

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
}

it should compile without an error.

On a sidenote, sth. like

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

would also not compile, as e1 in this case is 0 (as A is the first enum 'option') and therefore false which means that the second state would never be reached (disregarding whether the second statement would even be valid in this case)

I would define values for Enum like this and compare with ===

const enum AnimalInfo {
Tiger = "Tiger",
Lion = "Lion"
}


let tigerStr = "Tiger";


if (tigerStr === AnimalInfo.Tiger) {
console.log('true');
} else {
console.log('false');
}

In typescript an example enum:

enum Example {
type1,
type2
};

is transformed to javascript into this object:

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

I had many problems with comparison enums in typescript. This simple script solves the problem:

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

then in javascript, the object is transformed into:

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

If you don't need to use enums - it's better not to use. Typescript has more advanced types, more here: https://www.typescriptlang.org/docs/handbook/advanced-types.html You can use instead:

type Example = 'type1' | 'type2';

Type casting enums to strings is a very valuable technique.

For example;

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