typescript 如何以编程方式枚举枚举类型?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21293063/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-11 01:11:25  来源:igfitidea点击:

How to programmatically enumerate an enum type?

enumstypescript

提问by David Cuccia

Say I have a TypeScript enum, MyEnum, as follows:

假设我有一个 TypeScript enum, MyEnum,如下所示:

enum MyEnum {
    First,
    Second,
    Third
}

What would be the best way in TypeScript 0.9.5 to produce an array of the enumvalues? Example:

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?

回答by David Sherret

This is the JavaScript output of that enum:

这是该枚举的 JavaScript 输出:

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

Which is an object like this:

这是一个这样的对象:

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

Enum Members with String Values

具有字符串值的枚举成员

TypeScript 2.4 added the ability for enums to possibly have string enum member values. So it's possible to end up with an enum that look like the following:

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 = {}));

Getting Member Names

获取成员名称

We can look at the example immediately above to try to figure out how to get the enum members:

我们可以看看上面的例子来尝试找出如何获取枚举成员:

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

Here's what I came up with:

这是我想出的:

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

Member Values

会员价值

Once, we have the names, we can loop over them to get the corresponding value by doing:

一旦有了名称,我们就可以通过执行以下操作来遍历它们以获取相应的值:

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

Extension Class

扩展类

I think the best way to do this is to create your own functions (ex. EnumEx.getNames(MyEnum)). You can't add a function to an enum.

我认为最好的方法是创建自己的函数(例如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);
    }
}

回答by tanguy_k

With TypeScript >= 2.4you can define string enums:

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

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

JavaScript ES5 output:

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 = {}));

Which is an object like this:

这是一个这样的对象:

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

Thus, in the case of string enums, no need to filter things, Object.keys(Color)and Object.values(Color)(*) are enough:

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

const colorKeys = Object.keys(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
*/

See online example on TypeScript playground

请参阅TypeScript 操场上的在线示例

(*) Polyfill needed for old browsers, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Object/values#Browser_compatibility

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

回答by Rich

You can add functions to get the names and indices of the enum:

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

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]

Note that you couldjust export the _namesand _indicesconsts rather than exposing them through an exported function, but because the exported members are members of the enum it is arguably clearer to have them as functions so they are not confused with the actual enum members.

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

It would be nice if TypeScript generated something like this automatically for all enums.

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

回答by x0n

There is no concept of RTTI (runtime type information) in TypeScript (think: reflection) so in order to do this, knowledge of the transpiled JavaScript is required. So, assuming TypeScript 0.95:

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

enum MyEnum {
    First, Second, Third
}

becomes:

变成:

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

So, this is modeled as a regular object in javascript, where MyEnum.0 == "First"and MyEnum.First == 0. So, to enumerate all of the enum names, you need to get all properties that belong to the object and that are also not numbers:

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

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

Ok, so now I've told you how to do it, I'm allowed to tell you this is a bad idea. You're not writing a managed language, so you can't bring these habits. It's still just plain old JavaScript. If I wanted to use a structure in JavaScript to populate some kind of choices list, I would use a plain old array. An enum is not the right choice here, pun intended. The goal of TypeScript is to generate idiomatic, pretty JavaScript. Using enums in this way does not preserve this goal.

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

回答by Slava Shpitalny

I used the solution proposed by David Sherret and wrote an npm library you can use named enum-values...

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

Git: enum-values

Git:枚举值

// 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);

回答by Venryx

A one-liner to get a list of entries (key-value objects/pairs):

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

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

回答by Chklang

If you want to associate strings values to your enum these methods don't works. To have a generic function you can do :

如果您想将字符串值与您的枚举相关联,这些方法不起作用。要拥有通用功能,您可以执行以下操作:

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

It's works because TypeScript will add keys in first step, and values in second step.

这是有效的,因为 TypeScript 将在第一步添加键,在第二步添加值。

In TypeScript it's:

在打字稿中,它是:

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);

回答by joe

enum MyEnum {
    First, Second, Third, NUM_OF_ENUMS
}

for(int i = 0; i < MyEnum.NUM_OF_ENUMS; ++i) {
    // do whatever you need to do.
}

回答by 2grit

joe's answerjust made me realize that is much more easier to rely on the first N numeric keys than making more complex testings:

乔的回答让我意识到依赖前 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))

回答by Suben Saha

for nodejs:

对于 nodejs:

const { isNumber } = require('util');

Object.values(EnumObject)
      .filter(val => isNumber(val))
      .map(val => {
         // do your stuff
      })