วิธีการระบุประเภท enum โดยทางโปรแกรม?


91

บอกว่าผมมี typescript enum, 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 ของ enum นั้น:

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
}

Enum สมาชิกที่มีค่าสตริง

TypeScript 2.4 เพิ่มความสามารถสำหรับ enums ที่อาจมีค่าสมาชิกสตริง enum ดังนั้นจึงเป็นไปได้ที่จะลงเอยด้วย enum ที่มีลักษณะดังต่อไปนี้:

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

การรับชื่อสมาชิก

เราสามารถดูตัวอย่างด้านบนได้ทันทีเพื่อลองหาวิธีรับสมาชิก enum:

{
    "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)) คุณไม่สามารถเพิ่มฟังก์ชันให้กับ enum

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ฉันทำอะไรผิดหรือเปล่า?
ปีเตอร์

1
@Peter ฉันอัปเดตคำตอบเพื่อรวมข้อมูลเกี่ยวกับสตริง enums นอกจากนี้คุณจะต้องใช้for ofคำสั่งแทนfor in
David Sherret

24

ด้วยTypeScript> = 2.4คุณสามารถกำหนด enums สตริง:

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"
}

ดังนั้นในกรณีของสตริง enums ไม่จำเป็นต้องกรองสิ่งต่าง ๆ 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

(*) 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

1
@Jonas ฉันแก้ไขมันด้วยนักแสดง:Object.keys(Color) as (keyof typeof Color)[]
tanguy_k

9

คุณสามารถเพิ่มฟังก์ชันเพื่อรับชื่อและดัชนีของ 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]

โปรดทราบว่าคุณสามารถเพียงแค่ส่งออก_namesและ_indicesconsts แทนที่จะเปิดเผยผ่านฟังก์ชันที่ส่งออก แต่เนื่องจากสมาชิกที่ส่งออกเป็นสมาชิกของ enum จึงมีเนื้อหาชัดเจนกว่าที่จะมีเป็นฟังก์ชันดังนั้นจึงไม่สับสนกับสมาชิก enum จริง

คงจะดีถ้า TypeScript สร้างสิ่งนี้โดยอัตโนมัติสำหรับ enums ทั้งหมด


8

ไม่มีแนวคิดเกี่ยวกับ RTTI (ข้อมูลประเภทรันไทม์) ใน TypeScript (คิดว่า: การสะท้อน) ดังนั้นในการดำเนินการนี้จำเป็นต้องมีความรู้เกี่ยวกับ 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";
}

ดังนั้นสิ่งนี้จึงถูกจำลองเป็นวัตถุทั่วไปในจาวาสคริปต์โดยที่MyEnum.0 == "First"และMyEnum.First == 0. ดังนั้นในการระบุชื่อ enum ทั้งหมดคุณต้องได้รับคุณสมบัติทั้งหมดที่เป็นของวัตถุและนั่นไม่ใช่ตัวเลขด้วย:

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

ตกลงดังนั้นตอนนี้ผมเคยบอกคุณว่าจะทำฉันได้รับอนุญาตให้บอกคุณนี้เป็นความคิดที่ไม่ดี คุณไม่ได้เขียนภาษาที่มีการจัดการดังนั้นคุณจึงไม่สามารถนำนิสัยเหล่านี้มาใช้ได้ ยังคงเป็นเพียง JavaScript แบบเก่า ถ้าฉันต้องการใช้โครงสร้างใน JavaScript เพื่อเติมข้อมูลรายการตัวเลือกบางประเภทฉันจะใช้อาร์เรย์เก่าธรรมดา enum ไม่ใช่ตัวเลือกที่ถูกต้องที่นี่เล่นสำนวนตั้งใจ เป้าหมายของ TypeScript คือการสร้าง JavaScript ที่เป็นสำนวนและสวยงาม การใช้ enums ในลักษณะนี้ไม่ได้รักษาเป้าหมายนี้


5

ฉันใช้วิธีแก้ปัญหาที่เสนอโดย 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);

3

หนึ่งซับเพื่อรับรายการของรายการ (วัตถุคีย์ - ค่า / คู่):

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
สิ่งนี้ใช้ได้เฉพาะในกรณีที่ enum ของคุณไม่ได้กำหนดค่าใด ๆ (บรรจุไว้อย่างดีและเพิ่มขึ้น) ตัวอย่างเช่นค่า Enum อาจเป็น "First = 0x1000" หรือ "PageNotFound = 404" NUM_OF_ENUMS จะมากกว่าค่าที่กำหนดไว้มากที่สุดเสมอดังนั้น 0x1001 หรือ 405 ในตัวอย่างของฉัน
Aku

2

หากคุณต้องการเชื่อมโยงค่าสตริงกับ enum ของคุณวิธีการเหล่านี้ไม่ได้ผล หากต้องการมีฟังก์ชันทั่วไปคุณสามารถทำได้:

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

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
นี่เป็นข้อสันนิษฐานที่อันตรายเนื่องจากสามารถกำหนดค่าที่กำหนดให้กับ enums ได้และไม่จำเป็นต้องเพิ่มขึ้นและบรรจุอย่างดี ไม่ใช่เรื่องแปลกที่จะเห็นมาสก์บิตใน enum หรืออาจเป็นตารางรหัสข้อผิดพลาด HTML ที่เริ่มต้นที่ 400
Aku



0

วนซ้ำมากกว่า enum

String Enums ใช้ดีที่สุดสำหรับสิ่งนี้ นี่คือตัวอย่าง:

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