【问题标题】:How to programmatically enumerate an enum type?如何以编程方式枚举枚举类型?
【发布时间】:2014-02-13 02:37:09
【问题描述】:

假设我有一个TypeScript enumMyEnum,如下:

enum MyEnum {
    First,
    Second,
    Third
}

在 TypeScript 0.9.5 中生成 enum 值数组的最佳方法是什么?示例:

var choices: MyEnum[]; // or Array<MyEnum>
choices = MyEnum.GetValues(); // plans for this?
choices = EnumEx.GetValues(MyEnum); // or, how to roll my own?

【问题讨论】:

    标签: enums typescript


    【解决方案1】:

    这是该枚举的 JavaScript 输出:

    var MyEnum;
    (function (MyEnum) {
        MyEnum[MyEnum["First"] = 0] = "First";
        MyEnum[MyEnum["Second"] = 1] = "Second";
        MyEnum[MyEnum["Third"] = 2] = "Third";
    })(MyEnum || (MyEnum = {}));
    

    这是一个像这样的对象:

    {
        "0": "First",
        "1": "Second",
        "2": "Third",
        "First": 0,
        "Second": 1,
        "Third": 2
    }
    

    具有字符串值的枚举成员

    TypeScript 2.4 增加了枚举可能具有字符串枚举成员值的能力。因此,最终可能会得到如下所示的枚举:

    enum MyEnum {
        First = "First",
        Second = 2,
        Other = "Second"
    }
    
    // compiles to
    var MyEnum;
    (function (MyEnum) {
        MyEnum["First"] = "First";
        MyEnum[MyEnum["Second"] = 2] = "Second";
        MyEnum["Other"] = "Second";
    })(MyEnum || (MyEnum = {}));
    

    获取成员姓名

    我们可以看一下上面的例子,试图弄清楚如何获取枚举成员:

    {
        "2": "Second",
        "First": "First",
        "Second": 2,
        "Other": "Second"
    }
    

    这是我想出的:

    const e = MyEnum as any;
    const names = Object.keys(e).filter(k => 
        typeof e[k] === "number"
        || e[k] === k
        || e[e[k]]?.toString() !== k
    );
    

    会员价值观

    一旦有了名字,我们就可以遍历它们以获取相应的值:

    const values = names.map(k => MyEnum[k]);
    

    扩展类

    我认为最好的方法是创建自己的函数(例如EnumEx.getNames(MyEnum))。您不能向枚举添加函数。

    class EnumEx {
        private constructor() {
        }
    
        static getNamesAndValues(e: any) {
            return EnumEx.getNames(e).map(n => ({ name: n, value: e[n] as string | number }));
        }
    
        static getNames(e: any) {
            return Object.keys(e).filter(k => 
                typeof e[k] === "number"
                || e[k] === k
                || e[e[k]]?.toString() !== k
            );
        }
    
        static getValues(e: any) {
            return EnumEx.getNames(e).map(n => e[n] as string | number);
        }
    }
    

    【讨论】:

    • 奇怪的是(因为很多人都赞成这个答案),我不能让它在 TS Playground 中工作:shorturl.me/jJ8G2t我做错了什么吗?
    • @Peter 我更新了答案以包含有关字符串枚举的信息。此外,您需要使用 for of 语句而不是 for in
    【解决方案2】:

    TypeScript 中没有 RTTI(运行时类型信息)的概念(想想:反射),因此为了做到这一点,需要了解已转译的 JavaScript。所以,假设 TypeScript 0.95:

    enum MyEnum {
        First, Second, Third
    }
    

    变成:

    var MyEnum;
    (function(MyEnum) {
        MyEnum[MyEnum["First"] = 0] = "First";
        MyEnum[MyEnum["Second"] = 1] = "Second";
        MyEnum[MyEnum["Third"] = 2] = "Third";
    }
    

    因此,这在 javascript 中被建模为常规对象,其中 MyEnum.0 == "First"MyEnum.First == 0。因此,要枚举所有枚举名称,您需要获取属于该对象且也不是数字的所有属性:

    for (var prop in MyEnum) {         
        if (MyEnum.hasOwnProperty(prop) &&
            (isNaN(parseInt(prop)))) {
            console.log("name: " + prop);
        }
    }
    

    好的,现在我已经告诉你怎么做,我可以告诉你这是个坏主意。您不是在编写托管语言,因此您不能带来这些习惯。它仍然只是普通的旧 JavaScript。如果我想在 JavaScript 中使用一个结构来填充某种选择列表,我会使用一个普通的旧数组。枚举在这里不是正确的选择,双关语。 TypeScript 的目标是生成惯用的、漂亮的 JavaScript。以这种方式使用枚举并不能保持这个目标。

    【讨论】:

      【解决方案3】:
      enum MyEnum {
          First, Second, Third, NUM_OF_ENUMS
      }
      
      for(int i = 0; i < MyEnum.NUM_OF_ENUMS; ++i) {
          // do whatever you need to do.
      }
      

      【讨论】:

      • 这仅在您的枚举未定义任何值时才有效(它被很好地打包和增量)。例如,枚举值可能是“First = 0x1000”或“PageNotFound = 404”。 NUM_OF_ENUMS 将始终比最大定义值大一,因此在我的示例中为 0x1001 或 405。
      【解决方案4】:

      joe's answer 让我意识到依赖前 N 个数字键比进行更复杂的测试要容易得多:

      function getEnumMembers(myEnum): string[]
      {
          let members = []
          for(let i:number = 0; true; i++) {
              if(myEnum[i] === undefined) break
              members.push(myEnum[i])
          }
      
          return members
      }
      
      enum Colors {
          Red, Green, Blue
      }
      
      console.log(getEnumMembers(myEnum))
      

      【讨论】:

      • 这是一个危险的假设,因为可以定义分配给枚举的值,并且它们不需要增量和打包。例如,在枚举中看到位掩码并不罕见,或者可能是从 400 开始的 HTML 错误代码表。
      【解决方案5】:

      您可以添加函数来获取枚举的名称和索引:

      enum MyEnum {
        First,
        Second,
        Third
      }
      
      namespace MyEnum {
        function isIndex(key):boolean {
          const n = ~~Number(key);
          return String(n) === key && n >= 0;
        }
      
        const _names:string[] = Object
            .keys(MyEnum)
            .filter(key => !isIndex(key));
      
        const _indices:number[] = Object
            .keys(MyEnum)
            .filter(key => isIndex(key))
            .map(index => Number(index));
      
        export function names():string[] {
          return _names;
        }
      
        export function indices():number[] {
          return _indices;
        }
      }
      
      console.log("MyEnum names:", MyEnum.names());
      // Prints: MyEnum names: ["First", "Second", "Third"]
      
      console.log("MyEnum indices:", MyEnum.indices());
      // Prints: MyEnum indices: [0, 1, 2]
      

      请注意,您可以只导出 _names_indices 常量,而不是通过导出的函数公开它们,但由于导出的成员是枚举的成员,因此可以说更清楚它们作为函数,因此它们不会与实际的枚举成员混淆。

      如果 TypeScript 为所有枚举自动生成类似的东西,那就太好了。

      【讨论】:

        【解决方案6】:

        如果您想将字符串值关联到您的枚举,这些方法不起作用。要拥有通用功能,您可以这样做:

        function listEnum(enumClass) {
            var values = [];
            for (var key in enumClass) {
                values.push(enum[key]);
            }
            values.length = values.length / 2;
            return values;
        }
        

        这是可行的,因为 TypeScript 会在第一步添加键,在第二步添加值。

        在 TypeScript 中是:

        var listEnums = <T> (enumClass: any): T[]=> {
            var values: T[] = [];
            for (var key in enumClass) {
                values.push(enumClass[key]);
            }
            values.length = values.length / 2;
            return values;
        };
        
        var myEnum: TYPE[] = listEnums<TYPE>(TYPE);
        

        【讨论】:

          【解决方案7】:

          我使用了 David Sherret 提出的解决方案,并编写了一个你可以使用的 npm 库,命名为 enum-values...

          Git: enum-values

          // Suppose we have an enum
          enum SomeEnum {
            VALUE1,
            VALUE2,
            VALUE3
          }
          
          // names will be equal to: ['VALUE1', 'VALUE2', 'VALUE3']
          var names = EnumValues.getNames(SomeEnum);
          
          // values will be equal to: [0, 1, 2]
          var values = EnumValues.getValues(SomeEnum);
          

          【讨论】:

            【解决方案8】:

            获取条目列表(键值对象/对)的单行器:

            Object.keys(MyEnum).filter(a=>a.match(/^\D/)).map(name=>({name, value: MyEnum[name] as number}));
            

            【讨论】:

              【解决方案9】:

              使用TypeScript >= 2.4,您可以定义字符串枚举:

              enum Color {
                RED = 'Red',
                ORANGE = 'Orange',
                YELLOW = 'Yellow',
                GREEN = 'Green',
                BLUE = 'Blue',
                INDIGO = 'Indigo',
                VIOLET = 'Violet'
              }
              

              JavaScript ES5 输出:

              var Color;
              (function (Color) {
                  Color["RED"] = "Red";
                  Color["ORANGE"] = "Orange";
                  Color["YELLOW"] = "Yellow";
                  Color["GREEN"] = "Green";
                  Color["BLUE"] = "Blue";
                  Color["INDIGO"] = "Indigo";
                  Color["VIOLET"] = "Violet";
              })(Color || (Color = {}));
              

              这是一个像这样的对象:

              const Color = {
                "RED": "Red",
                "ORANGE": "Orange",
                "YELLOW": "Yellow",
                "GREEN": "Green",
                "BLUE": "Blue",
                "INDIGO": "Indigo",
                "VIOLET": "Violet"
              }
              

              因此,在字符串枚举的情况下,不需要过滤东西, Object.keys(Color)Object.values(Color) (*) 就够了:

              const colorKeys = Object.keys(Color) as (keyof typeof Color)[];
              console.log('colorKeys =', colorKeys);
              // ["RED", "ORANGE", "YELLOW", "GREEN", "BLUE", "INDIGO", "VIOLET"]
              
              const colorValues = Object.values(Color);
              console.log('colorValues =', colorValues);
              // ["Red", "Orange", "Yellow", "Green", "Blue", "Indigo", "Violet"]
              
              colorKeys.map(colorKey => {
                console.log(`color key = ${colorKey}, value = ${Color[colorKey]}`);
              });
              /*
              color key = RED, value = Red
              color key = ORANGE, value = Orange
              color key = YELLOW, value = Yellow
              color key = GREEN, value = Green
              color key = BLUE, value = Blue
              color key = INDIGO, value = Indigo
              color key = VIOLET, value = Violet
              */
              

              在线查看example on TypeScript playground

              (*) 旧浏览器需要 Polyfill,请参阅 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Object/values#Browser_compatibility

              【讨论】:

              • 这有错误Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'typeof Color'. No index signature with a parameter of type 'string' was found on type 'typeof Color'.
              • @Jonas 我已经用演员表修复了它:Object.keys(Color) as (keyof typeof Color)[]
              【解决方案10】:

              对于 nodejs:

              const { isNumber } = require('util');
              
              Object.values(EnumObject)
                    .filter(val => isNumber(val))
                    .map(val => {
                       // do your stuff
                    })
              

              【讨论】:

                【解决方案11】:

                遍历枚举

                字符串枚举最适合用于此目的。这是一个例子:

                // This is a string enum
                enum MyEnum {
                    First = 'First',
                    Second = 'Second',
                    Third = 'Third',
                }
                
                // An enum is a TS concept
                // However his MyEnum compiles to JS object:
                //  {
                //   "First": "First",
                //   "Second": "Second",
                //   "Third": "Third"
                // } 
                
                
                // Therefore we can get the keys in the following manner:
                const keysArray = Object.keys(MyEnum);
                
                for (const key of keysArray) {
                    console.log(key)
                }
                // [LOG]: "First" 
                // [LOG]: "Second" 
                // [LOG]: "Third" 
                

                【讨论】:

                  猜你喜欢
                  • 2016-12-10
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-09-16
                  • 1970-01-01
                  • 2014-07-23
                  • 1970-01-01
                  相关资源
                  最近更新 更多