通用枚举类型保护

Posted

技术标签:

【中文标题】通用枚举类型保护【英文标题】:Generic enum type guard 【发布时间】:2020-02-05 06:52:13 【问题描述】:

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

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


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

是否可以使这个通用化,以便我可以为许多不同的字符串枚举重用相同的检查逻辑?

【问题讨论】:

【参考方案1】:

你的意思是这样的?

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

SoisSomeEnum 从枚举对象生成类型保护函数。 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

【讨论】:

效果很好,谢谢! T[key of T] 的标注真的很有用——这是我需要学习的。 是否需要额外的“构造”功能?将其编写为单个函数时,我没有注意到任何问题。 问题是问怎么做这样的构造函数,所以在这个意义上是必要的。 ?【参考方案2】:

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

【讨论】:

如果能对您的解决方法进行一些解释,那就太好了。正则表达式和所有内容有点令人困惑。 我在这里找到了解释:***.com/a/56044932/8184729【参考方案3】:

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


【讨论】:

以上是关于通用枚举类型保护的主要内容,如果未能解决你的问题,请参考以下文章

MybatisPlus 学习通用枚举

[MyBatisPlus]通用枚举

[MyBatisPlus]通用枚举

[MyBatisPlus]通用枚举

Typescript 中的枚举类型是啥?

将枚举类型传递给具有整数值的转换器[重复]