【问题标题】:Map Typescript Enum地图打字稿枚举
【发布时间】:2017-05-09 13:17:15
【问题描述】:

我将如何映射打字稿枚举?例如,对于字符串,您可以这样做:

let arr = [ 'Hello', 'Goodbye' ];

arr.map(v => {
  if (v === 'Hello') {
    return ':)';
  } else if (v === 'Goodbye') {
    return ':(';
  }
); // [ ':)', ':(' ]

这当然不适用于枚举:

enum MyEnum { Hello, Goodbye };

MyEnum.map(v => {
  if (v === MyEnum.Hello) {
    return ':)';
  } else if (v === MyEnum.Goodbye) {
    return ':(';
  }
}); // does not work

理想情况下,我希望以一种通用的方式执行此操作,这样我就可以简单地获取我拥有的任何枚举并将其放入映射函数同时保留类型信息。用法可能如下所示:

map(MyEnum, v => {
  if (v === MyEnum.Hello) {
    return ':)';
  } else if (v === MyEnum.Goodbye) {
    return ':(';
  }
}); // [ ':)', ':(' ]

我一直在摆弄一个可以为我执行此操作的函数,但一直遇到问题让泛型恰到好处。

【问题讨论】:

    标签: javascript typescript enums


    【解决方案1】:

    解决这个问题的函数很简单。

    // you can't use "enum" as a type, so use this.
    type EnumType = { [s: number]: string };
    
    function mapEnum (enumerable: EnumType, fn: Function): any[] {
        // get all the members of the enum
        let enumMembers: any[] = Object.keys(enumerable).map(key => enumerable[key]);
    
        // we are only interested in the numeric identifiers as these represent the values
        let enumValues: number[] = enumMembers.filter(v => typeof v === "number");
    
        // now map through the enum values
        return enumValues.map(m => fn(m));
    }
    

    如您所见,我们首先需要获取枚举的所有键(MyEnum.Hello 在运行时实际上是 1),然后映射这些键,传递函数。

    使用也很简单(和你的例子一样,虽然我改了名字):

    enum MyEnum { Hello, Goodbye };
    
    let results = mapEnum(MyEnum, v => {
      if (v === MyEnum.Hello) {
        return ':)';
      } else if (v === MyEnum.Goodbye) {
        return ':(';
      }
    });
    
    console.log(results); // [ ':)', ':(' ]
    

    我们需要将枚举过滤为数字的原因是枚举的编译方式。

    你的枚举实际上是编译成这样的:

    var MyEnum;
    (function (MyEnum) {
        MyEnum[MyEnum["Hello"] = 0] = "Hello";
        MyEnum[MyEnum["Goodbye"] = 1] = "Goodbye";
    })(MyEnum || (MyEnum = {}));
    ;
    

    但是我们对 "Hello""Goodbye" 不感兴趣,因为我们不能在运行时使用它们。


    您还会在函数之前注意到一个有趣的type 语句。这是因为您不能将参数键入为someParameter: enum,您需要将其显式声明为number -> string 映射。

    【讨论】:

    • 这很棒,让我找到了一个可行的解决方案,但它在保留类型信息方面确实存在不足。添加一些泛型确实会有所帮助。然而,根据this 的说法,使用 typescript 似乎还不能完全使用带有枚举的泛型。
    • 您想要哪种类型的信息,具体来说,在哪里?
    • 这个函数签名是一个改进:function mapEnum<T>(enumerable: EnumType, fn:(v: any) => T):T[]。不幸的是v 仍然有任何类型。理想情况下,v 的类型将从可枚举的类型中推断出来。不幸的是,目前看来这是不可能的。 (不过我很想错!)
    • @Braden Snell 您可以将v 类型设置为数字。你也可以指定MyEnum,但它需要额外的泛型,我认为这不值得。
    【解决方案2】:

    我不会称它为通用,但我多次使用它,可能对其他人也很方便:

    type TMyEnum = ':)'|':(';
    class MyEnum {
        static Hello: TMyEnum = ':)';
        static Goodbye: TMyEnum = ':(';
    }
    console.log(MyEnum.Hello); // :)
    console.log(MyEnum.Goodbye); // :(
    

    现在您不需要任何映射函数,它可以按预期工作,但是您必须为每个枚举创建单独的类似类(这应该不是问题,因为无论如何您都会这样做)。我现在能想到的唯一缺点是你不能迭代它的属性。但直到现在这对我来说都不是问题,我不需要它。并且您可以在需要时将静态数组添加到类中。

    【讨论】:

      【解决方案3】:

      使用ts-enum-utilnpmgithub),它很简单,类型安全(使用泛型),并且可以为您跳过数字反向查找条目:

      import { $enum } from "ts-enum-util";
      
      enum MyEnum { Hello, Goodbye };
      
      $enum(MyEnum).map(v => {
          if (v === MyEnum.Hello) {
              return ':)';
          } else if (v === MyEnum.Goodbye) {
              return ':(';
          }
      }); // produces [':(', ':)']
      

      注意:ts-enum-util 始终根据已排序的枚举键的顺序进行迭代,以确保所有环境中的顺序一致。 Object.keys() 没有保证的顺序,因此不可能以跨平台保证的方式“按照定义的顺序”迭代枚举。 (更新:新版本的 ts-enum-util 现在保留了定义枚举的原始顺序)

      如果您使用的是字符串枚举,请将其与ts-string-visitornpmgithub)结合使用,以进行更通用的类型安全编译器检查,以确保您在 map 函数中处理所有可能的枚举值: (更新:新版本的 ts-enum-util 现在包含 ts-string-visitor 的功能,它现在也适用于数字枚举!)

      import { $enum } from "ts-enum-util";
      import { mapString } from "ts-string-visitor";
      
      enum MyEnum { Hello = "HELLO", Goodbye = "GOODBYE" };
      
      $enum(MyEnum).map(v => {
          // compiler error if you forget to handle a value, or if you
          // refactor the enum to have different values, etc.
          return mapString(v).with({
              [MyEnum.Hello]: ':)',
              [MyEnum.Goodby]: ':('
          });
      }); // produces [':(', ':)']
      

      【讨论】:

        【解决方案4】:

        要映射枚举,请执行以下操作:

        (Object.keys(MyEnum) as Array<keyof typeof MyEnum>).map((key) => {})
        

        【讨论】:

        • 添加到上面,如果你想把键作为一个数组,使用这个:``` Object.keys(MyEnum).filter((el) => { return isNaN(Number(el) ) }) ```
        【解决方案5】:

        这是您可以使用的工作功能。下面我将 ItemMaterial 传递给 getEnumKeys 函数并得到 ["YELLOW", "WHITE", "ROSE", "BLACK"]。

        类似地使用getEnumValues 函数来获取枚举的值。

        看看splitEnumKeysAndValues函数,看看这些变量是如何从枚举中提取出来的。

        enum ItemMaterial {
          YELLOW,
          WHITE,
          ROSE,
          BLACK,
        }
        
        
        const keys = getEnumKeys<typeof ItemMaterial>(ItemMaterial)
        const values = getEnumValues<typeof ItemMaterial, `${ItemMaterial}`>(ItemMaterial);
        
        function getEnumKeys<TypeofEnum>(value: TypeofEnum): keyof TypeofEnum  {
            const { values, keys } = splitEnumKeysAndValues(value);
        
            return keys as unknown as keyof TypeofEnum;
        }
        
        function getEnumValues<TypeofEnum, PossibleValues>(value: TypeofEnum): PossibleValues[]  {
            const { values, keys } = splitEnumKeysAndValues(value);
        
            return values as unknown as PossibleValues[];
        }
        
        function splitEnumKeysAndValues<T>(value: T): { keys: keyof T, values: Array<string | number> } {
          const enumKeys = Object.keys(value);
         
          const indexToSplit = enumKeys.length / 2
          const enumKeysKeyNames = enumKeys.slice(0, indexToSplit) as unknown as keyof T;
          const enumKeysKeyValues = enumKeys.slice(indexToSplit);
        
          return {
            keys: enumKeysKeyNames,
            values: enumKeysKeyValues,
          }
        }
        

        【讨论】:

          【解决方案6】:

          Typescript 中的映射对于编写更少的代码非常强大。 我最近一直在使用键值枚举映射,并且会推荐它! 这里有几个例子!

          基本枚举用法

          enum InlineStyle {
             "Bold",
             "Italic",
             "Underline"
          }
          
          type IS = keyof typeof InlineStyle
          
          // Example of looping
          Object.keys(InlineStyle) as Array<IS>).forEach((key) => {
            // code here
          })
          
          // Example of calling a function
          const styleInline = (style: IS) => {
            // code here
          }
          

          枚举键值用法

          enum ListStyle {
            "UL" = "List",
            "OL" = "Bullet points"
          }
          
          // Example of looping
          Object.entries(ListStyle).forEach(([key, value]) => {
            // code here
          })
          

          接口映射

          enum InlineStyle {
             "Bold" = "isBold",
             "Italic" = "isItalic",
             "Underline" = "isUnderlined"
          }
          
          type InlineStyleType = Record<InlineStyle, boolean>
          
          enum ListStyle {
            "UL",
            "OL"
          }
          
          type LS keyof typeof ListStyle
          
          interface HTMLBlock extends InlineStyleType {
            // This has extended with
            // isBold: boolean
            // isItalic: boolean
            // isUnderlined: boolean
          
            listType: LS
          }
          

          【讨论】:

            猜你喜欢
            • 2020-04-30
            • 1970-01-01
            • 2020-10-27
            • 1970-01-01
            • 2020-01-29
            • 2019-03-04
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多