简体   繁体   English

通用枚举类型保护

[英]Generic enum type guard

I can write a non-generic type guard to check if a given string is a member of a string enum like this:我可以编写一个非泛型类型保护来检查给定的字符串是否是字符串枚举的成员,如下所示:

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

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

Is it possible to make this generic, so that I could re-use the same checking logic for many different string enums?是否可以使这个通用化,以便我可以为许多不同的字符串枚举重用相同的检查逻辑?

You mean like this?你的意思是这样吗?

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

So isSomeEnum produces type guard functions from enum objects.所以isSomeEnum从枚举对象生成类型保护函数。 The type T[keyof T] means the types of the property values of T .类型T[keyof T]表示 T T属性值的类型

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

When you call isSomeEnum(MyEnum) , the type T is inferred as typeof MyEnum , and then T[keyof T] is the property values of that, which is MyEnum .当您调用isSomeEnum(MyEnum)时,类型T被推断为typeof MyEnum ,然后T[keyof T]是它的属性值,即MyEnum

Hope that helps.希望有帮助。 Good luck!祝你好运!

Link to code 链接到代码

TS string enums and number enums have very different JS emits. TS 字符串枚举和数字枚举有非常不同的 JS 发射。

The accepted answer works for OP's case of string enums.接受的答案适用于 OP 的字符串枚举情况。

But someone using number enums may naively think that it will also work for their use case.但是使用数字枚举的人可能天真地认为它也适用于他们的用例。 Be careful.当心。

//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 操场

A variation on top of @jcalz answer is just a single and plain function that can be directly used: @jcalz 答案之上的一个变体只是一个可以直接使用的简单的 function:

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

As Justin AnyhowStep observed, this function works only on string enums, sot I put the T extends { [k: string]: string } clause.正如 Justin AnyhowStep 所观察到的,这个 function 仅适用于字符串枚举,所以我放置了T extends { [k: string]: string }子句。 In this way we havethis behaviour:这样我们就有了这种行为:

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

I didn't find such a package on npm and I thought it would be nice to create one: https://www.npmjs.com/package/is-some-enum .我在 npm 上没有找到这样的 package,我认为创建一个会很好: https://www.npmjs.com/package/is-en . I think it will help someone:)我认为它会帮助某人:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM