检查TypeScript中enum中是否存在value

我收到一个数字type = 3,必须检查它是否存在于这个enum中:

export const MESSAGE_TYPE = {
INFO: 1,
SUCCESS: 2,
WARNING: 3,
ERROR: 4,
};

我发现最好的方法是将所有Enum值作为一个数组,并在其上使用indexOf。但结果代码不是很容易读懂:

if( -1 < _.values( MESSAGE_TYPE ).indexOf( _.toInteger( type ) ) ) {
// do stuff ...
}

有更简单的方法吗?

349291 次浏览

这只适用于非const,基于数字的枚举。对于const枚举或其他类型的枚举,请参见this answer


如果你正在使用TypeScript,你可以使用实际的枚举。然后你可以使用in检查它。

export enum MESSAGE_TYPE {
INFO = 1,
SUCCESS = 2,
WARNING = 3,
ERROR = 4,
};


var type = 3;


if (type in MESSAGE_TYPE) {


}

这是因为当你编译上面的枚举时,它会生成下面的对象:

{
'1': 'INFO',
'2': 'SUCCESS',
'3': 'WARNING',
'4': 'ERROR',
INFO: 1,
SUCCESS: 2,
WARNING: 3,
ERROR: 4
}

如果你想让它与字符串enum一起工作,你需要使用Object.values(ENUM).includes(ENUM.value),因为字符串enum不是反向映射的,根据https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-4.html:

enum Vehicle {
Car = 'car',
Bike = 'bike',
Truck = 'truck'
}

就变成:

{
Car: 'car',
Bike: 'bike',
Truck: 'truck'
}

所以你只需要做:

if (Object.values(Vehicle).includes('car')) {
// Do stuff here
}

如果你得到一个错误:Property 'values' does not exist on type 'ObjectConstructor',那么你不是针对ES2017。您可以使用这个tsconfig。json配置:

"compilerOptions": {
"lib": ["es2017"]
}

或者你可以做任意类型的转换:

if ((<any>Object).values(Vehicle).includes('car')) {
// Do stuff here
}
export enum UserLevel {
Staff = 0,
Leader,
Manager,
}


export enum Gender {
None = "none",
Male = "male",
Female = "female",
}

log中的差异结果:

log(Object.keys(Gender))
=>
[ 'None', 'Male', 'Female' ]


log(Object.keys(UserLevel))
=>
[ '0', '1', '2', 'Staff', 'Leader', 'Manager' ]

解决方案,我们需要删除键作为一个数字。

export class Util {
static existValueInEnum(type: any, value: any): boolean {
return Object.keys(type).filter(k => isNaN(Number(k))).filter(k => type[k] === value).length > 0;
}
}

使用

// For string value
if (!Util.existValueInEnum(Gender, "XYZ")) {
//todo
}


//For number value, remember cast to Number using Number(val)
if (!Util.existValueInEnum(UserLevel, 0)) {
//todo
}

对于你的问题,有一个非常简单易行的解决方法:

var districtId = 210;


if (DistrictsEnum[districtId] != null) {


// Returns 'undefined' if the districtId not exists in the DistrictsEnum
model.handlingDistrictId = districtId;
}
export enum YourEnum {
enum1 = 'enum1',
enum2 = 'enum2',
enum3 = 'enum3',
}


const status = 'enumnumnum';


if (!Object.values(YourEnum)?.includes(status)) {
throw new UnprocessableEntityResponse('Invalid enum val');
}
enum ServicePlatform {
UPLAY = "uplay",
PSN = "psn",
XBL = "xbl"
}

就变成:

{ UPLAY: 'uplay', PSN: 'psn', XBL: 'xbl' }

所以

ServicePlatform.UPLAY in ServicePlatform // false

解决方案:

ServicePlatform.UPLAY.toUpperCase() in ServicePlatform // true

sandersn说最好的方法是:

Object.values(MESSAGE_TYPE).includes(type as MESSAGE_TYPE)

对于任何来这里验证字符串是否是enum值之一并对其进行类型转换的人来说,我写了这个函数,它返回正确的类型,如果字符串不在enum中则返回undefined

function keepIfInEnum<T>(
value: string,
enumObject: { [key: string]: T }
) {
if (Object.values(enumObject).includes((value as unknown) as T)) {
return (value as unknown) as T;
} else {
return undefined;
}
}

举个例子:

enum StringEnum {
value1 = 'FirstValue',
value2 = 'SecondValue',
}
keepIfInEnum<StringEnum>('FirstValue', StringEnum)  // 'FirstValue'
keepIfInEnum<StringEnum>('OtherValue', StringEnum)  // undefined

类型断言是不可避免的。跟进

enum Vehicle {
Car = 'car',
Bike = 'bike',
Truck = 'truck'
}

我发现了一个没有被提及的替代方法,所以我想分享一下我的解决方法:

const someString: Vehicle | string = 'car';
const inEnum = (Object.values(Vehicle) as string[]).includes(someString);

我发现这更真实,因为我们通常是类型安全的(与字符串),并希望将其与enum进行比较;将它类型转换为any(原因:永远不要这样做)或Vehicle(原因:可能不真实)有点鲁莽。相反,将Object.values()输出类型转换为字符串数组实际上非常真实。

更新:

我发现,每当我需要检查一个值是否存在于枚举中,我并不真的需要枚举,类型是一个更好的解决方案。所以我原来答案中的enum变成:

export type ValidColors =
| "red"
| "orange"
| "yellow"
| "green"
| "blue"
| "purple";

最初的回答:

为了清晰起见,我喜欢将valuesincludes调用分开到单独的行上。这里有一个例子:

export enum ValidColors {
Red = "red",
Orange = "orange",
Yellow = "yellow",
Green = "green",
Blue = "blue",
Purple = "purple",
}


function isValidColor(color: string): boolean {
const options: string[] = Object.values(ValidColors);
return options.includes(color);
}

如果你在那里找不到如何检查联合包含的具体值,有解决方案:

// source enum type
export const EMessagaType = {
Info,
Success,
Warning,
Error,
};


//check helper
const isUnionHasValue = <T extends number>(union: T, value: T) =>
(union & value) === value;




//tests
console.log(
isUnionHasValue(EMessagaType.Info | EMessagaType.Success),
EMessagaType.Success);


//output: true




console.log(
isUnionHasValue(EMessagaType.Info | EMessagaType.Success),
EMessagaType.Error);


//output: false

下面的函数返回另一个函数,该函数作为输入enum的类型谓词(假设它是字符串样式的enum)。

function constructEnumPredicate<RuntimeT extends string, EnumClass extends {[key: string]: RuntimeT}>(enumClass: EnumClass): (maybeEnum: string) => maybeEnum is EnumClass[keyof EnumClass] {
const reverseMapping: {[key: string]: boolean} = {};


for (const enumVal in enumClass) {
const enumStr = enumClass[enumVal];
reverseMapping[enumStr] = true;
}


function result(maybeEnum: any): maybeEnum is EnumClass[keyof EnumClass] {
return !!reverseMapping[maybeEnum];
}


return result;
}

它可以在TypeScript 4.2.4中工作,但我还没有测试早期版本。

主要有趣的部分是EnumClass[keyof EnumClass]返回类型。当这样的类型是TypeScript中的enum时,它会返回enum的原始类型,其中EnumClass是运行时enum类的类型。

对于如何使用这个结构的例子,假设我们有以下枚举:

enum Direction {
Left = "<-",
Right = "->"
}

Direction既是类型,也是运行时对象。我们可以为Direction生成一个类型谓词,并像这样使用它:

const isDirection = constructEnumPredicate(Direction);
function coerceDirection(maybeDir: string): Direction {
// Since we make a type predicate rather than just a normal predicate,
// no explicit type casting is necessary!
return isDirection(maybeDir) ? maybeDir : Direction.Left;
}