通用枚举类型防护

时间:2019-10-08 00:19:04

标签: typescript

我可以编写一个非泛型类型防护,以检查给定的字符串是否是字符串枚举的成员,如下所示:

enum MyEnum {
  Thing1 = 'thing one',
  Thing2 = 'thing two',
}

const isMyEnum = (token: any): token is MyEnum => {
  return Object.values(MyEnum).includes(token as MyEnum);
};

是否有可能使它通用,以便我可以对许多不同的字符串枚举重新使用相同的检查逻辑?

3 个答案:

答案 0 :(得分:4)

TS字符串枚举和数字枚举具有完全不同的JS发出。

可接受的答案适用于OP的字符串枚举情况。

但是使用数字枚举的人可能会天真的认为它也适用于他们的用例。小心点。

//number enum here
enum E {
  A,
  B,
  C,
}

const isSomeEnum = <T>(e: T) => (token: any): token is T[keyof T] =>
  (Object as any).values(e).includes(token as T[keyof T]);

console.log(isSomeEnum(E)("A")); //expected false, actual true
console.log(isSomeEnum(E)(0));   //expected true , actual true

function isSomeEnum2<T> (e: T) : (token: unknown) => token is T[keyof T] {
  const keys = Object.keys(e)
    .filter((k) => {
      return !/^\d/.test(k);
    });
  const values = keys.map((k) => {
    return (e as any)[k];
  });
  return (token: unknown): token is T[keyof T] => {
    return values.includes(token);
  };
};

console.log(isSomeEnum2(E)("A")); //expected false, actual false
console.log(isSomeEnum2(E)(0));   //expected true , actual true

Playground

答案 1 :(得分:2)

您的意思是这样吗?

const isSomeEnum = <T>(e: T) => (token: any): token is T[keyof T] =>
    Object.values(e).includes(token as T[keyof T]);

So isSomeEnum从枚举对象生成类型保护函数。类型T[keyof T]表示types of the property values of T

const isMyEnum = isSomeEnum(MyEnum);
// const isMyEnum: (token: any) => token is MyEnum

调用isSomeEnum(MyEnum)时,类型T被推断为typeof MyEnum,然后T[keyof T]是其属性值,即MyEnum

希望有帮助。祝你好运!

Link to code

答案 2 :(得分:1)

@jcalz 答案之上的一个变体只是一个可以直接使用的简单函数:

const isEnumValue = <T extends { [k: string]: string }>(something: any, enumObject: T): something is T[keyof T] =>
    typeof something === 'string' && Object.values(enumObject).includes(something);

正如 Justin AnyhowStep 所观察到的,此函数仅适用于字符串枚举,因此我放置了 T extends { [k: string]: string } 子句。这样我们就有了这种行为:

enum StringEnum {
    A = 'aaa',
    B = 'bbb',
}

enum NumberEnum {
    A,
    B,
}

let a;

if (isEnumValue(a, StringEnum)) {
    if (a === 'SOMETHING') {
        // compiler complains:
        // This condition will always return 'false' since the types 'StringEnum' and '"SOMETHING"' have no overlap.
    }
}
if (isEnumValue(a, NumberEnum)) {
    // compiler complains:
    // Argument of type 'typeof NumberEnum' is not assignable to parameter of type '{ [k: string]: string; }'.
    // Property 'A' is incompatible with index signature.
    // Type 'NumberEnum' is not assignable to type 'string'.
}