web-dev-qa-db-fra.com

Comment énumérer par programme un type enum?

Disons que j'ai un TypeScript enum, MyEnum, comme suit:

enum MyEnum {
    First,
    Second,
    Third
}

Quel serait le meilleur moyen dans TypeScript 0.9.5 de produire un tableau des valeurs enum? Exemple:

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

Voici la sortie JavaScript de cette énumération:

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

Quel est un objet comme celui-ci:

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

Noms des membres

Pour obtenir les noms des membres enum, nous pouvons filtrer les clés de l'objet lorsque la valeur correspondante est un nombre. Cela garantit que les noms avec la même valeur seront trouvés:

const names = Object.keys(MyEnum)
    .filter(k => typeof MyEnum[k] === "number") as string[];

Contient: ["First", "Second", "Third"]

valeurs membres

Alors que pour obtenir les valeurs de membre enum, nous pouvons filtrer les valeurs de l'objet par tout ce qui est un nombre:

const values = Object.keys(MyEnum)
    .map(k => MyEnum[k])
    .filter(v => typeof v === "number") as number[];

Contient: [0, 1, 2]

Classe d'extension

Je pense que la meilleure façon de faire est de créer vos propres fonctions (ex. EnumEx.getNames(MyEnum)). Vous ne pouvez pas ajouter une fonction à une énumération.

class EnumEx {
    private constructor() {
    }

    static getNamesAndValues<T extends number>(e: any) {
        return EnumEx.getNames(e).map(n => ({ name: n, value: e[n] as T }));
    }

    static getNames(e: any) {
        return Object.keys(e).filter(k => typeof e[k] === "number") as string[];
    }

    static getValues<T extends number>(e: any) {
        return Object.keys(e)
            .map(k => e[k])
            .filter(v => typeof v === "number") as T[];
    }
}
179
David Sherret

Avec TypeScript> = 2.4 , vous pouvez définir des énumérations de chaîne:

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

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

Quel est un objet comme celui-ci:

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

Ainsi, dans le cas d'énumérations de chaîne, inutile de filtrer les éléments, Object.keys(Color) et Object.values(Color) (*) ​​suffisent:

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
*/

Voir en ligne exemple sur un terrain de jeu TypeScript

(*) Polyfill nécessaire pour les anciens navigateurs, voir https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Object/values#Browser_compatibility

19
tanguy_k

Vous pouvez ajouter des fonctions pour obtenir les noms et les index de l'énumération:

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]

Notez que vous pourriez simplement exporter le _names et _indices consts plutôt que de les exposer via une fonction exportée, mais comme les membres exportés sont des membres de l’énum, ​​il est sans doute plus clair de les avoir comme fonctions afin de ne pas les confondre avec les membres d’énum effectifs.

Ce serait bien si TypeScript générait quelque chose comme ceci automatiquement pour toutes les énumérations.

8
Rich

Il n'y a pas de concept de RTTI (informations de type à l'exécution) dans TypeScript (pensez: réflexion), vous devez donc connaître le code JavaScript transpilé. Donc, en supposant que TypeScript 0.95:

enum MyEnum {
    First, Second, Third
}

devient:

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

Donc, ceci est modélisé comme un objet régulier en javascript, où MyEnum.0 == "First" et MyEnum.First == 0. Donc, pour énumérer tous les noms enum, vous devez obtenir toutes les propriétés qui appartiennent à l'objet et qui ne sont pas non plus des nombres:

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

Ok, alors maintenant je vous ai dit comment faire, je suis autorisé à vous dire c'est une mauvaise idée. Vous n'écrivez pas un langage géré, vous ne pouvez donc pas adopter ces habitudes. C'est toujours du vieux JavaScript. Si je voulais utiliser une structure en JavaScript pour remplir une sorte de liste de choix, j'utiliserais un ancien tableau simple. Une enum n'est pas le bon choix ici, le jeu de mots voulu. Le but de TypeScript est de générer du joli JavaScript idiomatique. Utiliser des enums de cette manière ne préserve pas cet objectif.

8
x0n

J'ai utilisé la solution proposée par David Sherret et écrit une bibliothèque npm que vous pouvez utiliser nommée 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);
5
Slava Shpitalny

Si vous souhaitez associer des valeurs de chaînes à votre enum, ces méthodes ne fonctionnent pas. Pour avoir une fonction générique, vous pouvez faire:

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

Cela fonctionne parce que TypeScript ajoutera des clés dans la première étape et des valeurs dans la deuxième étape.

Dans TypeScript c'est:

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

Une ligne pour obtenir une liste d'entrées (objets/paires clé-valeur):

Object.keys(MyEnum).filter(a=>a.match(/^\D/)).map(name=>({name, value: MyEnum[name] as number}));
2
Venryx
enum MyEnum {
    First, Second, Third, NUM_OF_ENUMS
}

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

réponse de joe vient de me faire comprendre qu'il est beaucoup plus facile de s'appuyer sur les N premières touches numériques que de faire des tests plus complexes:

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))
1
kbtzr