【问题标题】: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);
};

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

【问题讨论】:

    标签: typescript


    【解决方案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

    【讨论】:

    • 如果能对您的解决方法进行一些解释,那就太好了。正则表达式和所有内容有点令人困惑。
    • 我在这里找到了解释:stackoverflow.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'.
    }
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-17
      • 2022-01-23
      • 1970-01-01
      • 1970-01-01
      • 2022-09-23
      • 2012-09-16
      • 1970-01-01
      • 2017-02-05
      相关资源
      最近更新 更多