プログラムで列挙型を列挙する方法は?


91

次のように、TypeScriptenumがあるMyEnumとします。

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?

回答:


193

これは、その列挙型の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プレイグラウンドでこれを機能させることができません:shorturl.me/jJ8G2t何か問題がありますか?
ピーター

1
@Peter回答を更新して、文字列列挙型に関する情報を含めました。また、次のfor of代わりにステートメントを使用することをお勧めしますfor in
DavidSherret20年

24

活字体> = 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
*/

TypeScriptプレイグラウンドのオンライン例を参照してください

(*)古いブラウザにはポリフィルが必要です。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'.
ジョナス

1
:私はキャストでそれを修正した@JonasObject.keys(Color) as (keyof typeof Color)[]
tanguy_k

9

関数を追加して、列挙型の名前とインデックスを取得できます。

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]

エクスポートされた関数を介して公開するのではなく、およびconstsをエクスポートすることもできます、エクスポートされたメンバーは列挙型のメンバーであるため、実際の列挙型メンバーと混同しないように、関数として持つ方が間違いなく明確です。_names_indices

TypeScriptがすべての列挙型に対してこのようなものを自動的に生成するといいでしょう。


8

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を生成することです。このように列挙型を使用しても、この目標は維持されません。


5

私はDavidSherretによって提案されたソリューションを使用し、名前付きで使用できるnpmライブラリを作成しました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);

3

エントリ(Key-Valueオブジェクト/ペア)のリストを取得するためのワンライナー:

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

2
enum MyEnum {
    First, Second, Third, NUM_OF_ENUMS
}

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

5
これは、列挙型が値を定義していない場合にのみ機能します(十分にパックされており、増分です)。列挙値は、たとえば「First = 0x1000」または「PageNotFound = 404」のようになります。NUM_OF_ENUMSは、定義された最大値より常に1大きいため、私の例では0x1001または405です。
aku

2

文字列値を列挙型に関連付けたい場合、これらのメソッドは機能しません。ジェネリック関数を使用するには、次のことができます。

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

TypeScriptは最初のステップでキーを追加し、2番目のステップで値を追加するため、これは機能します。

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

1

joeの答えは、より複雑なテストを行うよりも、最初の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))

4
列挙型に割り当てられた値を定義することが可能であり、それらを増分して適切にパックする必要がないため、これは危険な仮定です。たとえば、列挙型にビットマスクが表示されたり、400で始まるHTMLエラーコードの表が表示されたりすることは珍しくありません
Aku


0

nodejsの場合:

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

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

0

列挙型を反復処理

これには、文字列列挙型が最適です。次に例を示します。

// 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" 
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.