我收到一个数字类型= 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 ...
}

有更简单的方法吗?


当前回答

下面的函数返回另一个函数,该函数作为输入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;
}

其他回答

这只适用于非const,基于数字的枚举。有关const enum或其他类型的enum,请参见此答案


如果你使用的是TypeScript,你可以使用一个实际的enum。然后你可以用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值之一并对其进行类型转换的人来说,我写了这个函数,它返回正确的类型,如果字符串不在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的类型谓词(假设它是字符串样式的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;
}

如果你想让它与字符串ENUM一起工作,你需要使用Object.values(ENUM).includes(enumenumvalue),因为字符串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
}

如果你得到一个错误:属性“values”在类型“ObjectConstructor”上不存在,那么你不是针对ES2017。您可以使用这个tsconfig。json配置:

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

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

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

根据sandersn的说法,最好的方法是:

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