Bir numaralandırma türü programlı olarak nasıl numaralandırılır?


91

Ben daktilo var ki enum, MyEnumyanı şu:

enum MyEnum {
    First,
    Second,
    Third
}

TypeScript 0.9.5'te enumdeğerler dizisi oluşturmanın en iyi yolu nedir ? Misal:

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

Yanıtlar:


193

Bu, o numaralandırmanın JavaScript çıktısıdır:

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

Bunun gibi bir nesne:

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

Dize Değerlerine Sahip Enum Üyeleri

TypeScript 2.4, numaralandırmalar için muhtemelen dize enum üye değerlerine sahip olma özelliğini ekledi. Bu nedenle, aşağıdaki gibi görünen bir enum ile sonuçlanmak mümkündür:

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

Üye Adlarını Alma

Enum üyelerini nasıl elde edeceğimizi bulmaya çalışmak için hemen yukarıdaki örneğe bakabiliriz:

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

İşte bulduğum şey:

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

Üye Değerleri

İsimlere sahip olduğumuzda, bunları yaparak karşılık gelen değeri elde etmek için bunların üzerinden geçebiliriz:

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

Uzatma Sınıfı

Bunu yapmanın en iyi yolunun kendi işlevlerinizi yaratmak olduğunu düşünüyorum (örn. EnumEx.getNames(MyEnum)). Bir numaralandırmaya işlev ekleyemezsiniz.

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

Merakla (birçok insan bu cevaba oy verdiğinden), bunu TS Playground'da çalıştıramıyorum: shorturl.me/jJ8G2t Yanlış bir şey mi yapıyorum?
Peter

1
@Peter Yanıtı dize numaralandırmaları hakkında bilgi içerecek şekilde güncelledim. Ayrıca, bir kullanmak isteyeceksiniz for ofbir yerine açıklamafor in
David Sherret

24

İle typescript> = 2.4 Eğer dize Çeteleler tanımlayabilirsiniz:

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

JavaScript ES5 çıktısı:

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

Bunun gibi bir nesne:

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

Bu nedenle, dize numaralandırmaları durumunda, nesneleri filtrelemeye gerek yoktur Object.keys(Color)ve Object.values(Color)(*) yeterlidir:

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 oyun alanında çevrimiçi örneğe bakın

(*) Eski tarayıcılar için Polyfill gereklidir, bkz. Https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Object/values#Browser_compatibility


Hata varElement 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

1
@Jonas Bir kadroyla düzelttim:Object.keys(Color) as (keyof typeof Color)[]
tanguy_k

9

Numaralamanın adlarını ve dizinlerini almak için işlevler ekleyebilirsiniz:

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]

O Not olabilir sadece ihracat _namesve _indicesbir ihraç fonksiyonu aracılığıyla teşhir ziyade consts ancak ihraç üyeleri enum üyeleridir çünkü onlar gerçek enum üyeleri ile karışıklığa neden olmaması için fonksiyonları olarak onlara sahip olmak tartışmalı nettir.

TypeScript'in tüm numaralandırmalar için otomatik olarak böyle bir şey oluşturması iyi olurdu.


8

TypeScript'te (düşünün: yansıtma) RTTI (çalışma zamanı türü bilgisi) kavramı yoktur, bu nedenle bunu yapmak için, kopyalanmış JavaScript bilgisi gereklidir. Yani TypeScript 0.95 varsayarsak:

enum MyEnum {
    First, Second, Third
}

şu hale gelir:

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

Yani, bu javascript, düzenli nesne olarak modellenmiştir MyEnum.0 == "First"ve MyEnum.First == 0. Bu nedenle, tüm enum adlarını numaralandırmak için, nesneye ait olan ve aynı zamanda sayı olmayan tüm özellikleri almanız gerekir:

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

Tamam, şimdi size nasıl yapılacağını söyledim , bunun kötü bir fikir olduğunu söylememe izin var . Yönetilen bir dil yazmıyorsunuz, bu yüzden bu alışkanlıkları getiremezsiniz. Hala sadece eski bir JavaScript. JavaScript'te bir çeşit seçim listesi doldurmak için bir yapı kullanmak isteseydim, düz eski bir dizi kullanırdım. Burada bir sıralama doğru seçim değildir, kelime oyunu. TypeScript'in amacı, deyimsel, güzel JavaScript oluşturmaktır. Numaralandırmaları bu şekilde kullanmak, bu hedefi korumaz.


5

David Sherret tarafından önerilen çözümü kullandım ve kullanabileceğiniz bir npm kitaplığı yazdım enum-values...

Git: enum değerleri

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

Girişlerin bir listesini almak için tek satırlık bir (anahtar-değer nesneleri / çiftleri):

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
Bu yalnızca numaranız herhangi bir değer tanımlamadığında çalışır (iyi paketlenmiş ve artımlıdır). Enum değerleri örneğin "First = 0x1000" veya "PageNotFound = 404" olabilir. NUM_OF_ENUMS her zaman tanımlanan en büyük değerden bir büyük olacaktır, bu nedenle örneklerimde 0x1001 veya 405.
Aku

2

Dizge değerlerini numaranızla ilişkilendirmek istiyorsanız, bu yöntemler çalışmaz. Genel bir işleve sahip olmak için şunları yapabilirsiniz:

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

Çalışır çünkü TypeScript ilk adımda anahtarları ve ikinci adımda değerleri ekleyecektir.

TypeScript'te:

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'nun cevabı , ilk N sayısal tuşlara güvenmenin daha karmaşık testler yapmaktan çok daha kolay olduğunu fark etmemi sağladı:

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
Bu tehlikeli bir varsayımdır çünkü numaralandırmalara atanan değerleri tanımlamak mümkündür ve artımlı ve iyi paketlenmiş olmaları gerekmez. Örneğin, bir numaralandırmada bit maskeleri veya 400'de başlayan bir HTML hata kodları tablosu görmek alışılmadık bir durum değildir.
Aku


0

nodejs için:

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

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

0

Bir numaralandırma üzerinde yineleme

Dize Enümleri bunun için en iyi şekilde kullanılır. İşte bir örnek:

// 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" 
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.