Làm cách nào để liệt kê một kiểu enum theo chương trình?


90

Nói rằng tôi có một nguyên cảo enum, MyEnumnhư sau:

enum MyEnum {
    First,
    Second,
    Third
}

Cách tốt nhất trong TypeScript 0.9.5 để tạo ra một mảng các enumgiá trị là gì? Thí dụ:

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

Câu trả lời:


192

Đây là đầu ra JavaScript của enum đó:

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

Đó là một đối tượng như thế này:

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

Đăng ký thành viên với giá trị chuỗi

TypeScript 2.4 đã thêm khả năng cho enum có thể có các giá trị thành viên chuỗi enum. Vì vậy, có thể kết thúc với một enum giống như sau:

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

Nhận tên thành viên

Chúng ta có thể xem ngay ví dụ ở trên để cố gắng tìm ra cách lấy các thành viên enum:

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

Đây là những gì tôi nghĩ ra:

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

Giá trị thành viên

Khi đã có tên, chúng ta có thể lặp lại chúng để nhận giá trị tương ứng bằng cách thực hiện:

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

Lớp mở rộng

Tôi nghĩ rằng cách tốt nhất để làm điều này là tạo các hàm của riêng bạn (ví dụ EnumEx.getNames(MyEnum):). Bạn không thể thêm một hàm vào một 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);
    }
}

Thật kỳ lạ (vì nhiều người đã ủng hộ câu trả lời này), tôi không thể làm cho câu trả lời này hoạt động trong Sân chơi TS: shorturl.me/jJ8G2t Tôi có làm gì sai không?
Peter

1
@Peter Tôi đã cập nhật câu trả lời để bao gồm thông tin về chuỗi enums. Ngoài ra, bạn sẽ muốn sử dụng một for ofcâu lệnh thay vì câufor in
David Sherret

24

Với TypeScript> = 2.4, bạn có thể định nghĩa chuỗi enums:

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

JavaScript ES5 đầu ra:

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

Đó là một đối tượng như thế này:

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

Do đó, trong trường hợp chuỗi enums, không cần lọc mọi thứ Object.keys(Color)Object.values(Color)(*) là đủ:

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

Xem ví dụ trực tuyến trên sân chơi TypeScript

(*) Polyfill cần thiết cho các trình duyệt cũ, xem https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Object/values#Browser_compatibility


Điều này có lỗiElement 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 Tôi đã sửa nó với một dàn diễn viên:Object.keys(Color) as (keyof typeof Color)[]
tanguy_k

tuyệt vời cảm ơn bạn!
Jonas

9

Bạn có thể thêm các hàm để lấy tên và chỉ số của 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]

Lưu ý rằng bạn chỉ có thể xuất các _names_indiceskhuyết điểm thay vì hiển thị chúng thông qua một hàm được xuất, nhưng vì các thành viên được xuất là thành viên của enum nên rõ ràng hơn là có chúng dưới dạng hàm để chúng không bị nhầm lẫn với các thành viên enum thực.

Sẽ rất tuyệt nếu TypeScript tự động tạo ra một thứ như thế này cho tất cả các enum.


7

Không có khái niệm về RTTI (thông tin kiểu thời gian chạy) trong TypeScript (nghĩ: phản chiếu), vì vậy để làm được điều này, cần phải có kiến ​​thức về JavaScript đã chuyển đổi. Vì vậy, giả sử TypeScript 0.95:

enum MyEnum {
    First, Second, Third
}

trở thành:

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

Vì vậy, đây được mô hình hóa như một đối tượng thông thường trong javascript, ở đâu MyEnum.0 == "First"MyEnum.First == 0. Vì vậy, để liệt kê tất cả các tên enum, bạn cần lấy tất cả các thuộc tính thuộc về đối tượng và đó cũng không phải là số:

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

Ok, vậy bây giờ tôi đã cho bạn biết cách thực hiện, tôi được phép nói với bạn đây là một ý tưởng tồi . Bạn không viết một ngôn ngữ được quản lý, vì vậy bạn không thể mang những thói quen này. Nó vẫn chỉ là JavaScript cũ. Nếu tôi muốn sử dụng một cấu trúc trong JavaScript để điền vào một số loại danh sách lựa chọn, tôi sẽ sử dụng một mảng cũ thuần túy. Một enum không phải là lựa chọn phù hợp ở đây, ý định chơi chữ. Mục tiêu của TypeScript là tạo ra JavaScript đẹp, dễ hiểu. Sử dụng enum theo cách này không bảo toàn mục tiêu này.


5

Tôi đã sử dụng giải pháp do David Sherret đề xuất và viết một thư viện npm mà bạn có thể sử dụng có tên là enum-values...

Git: enum-giá trị

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

Một lớp lót để nhận danh sách các mục nhập (đối tượng / cặp khóa-giá trị):

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
Điều này chỉ hoạt động nếu enum của bạn không xác định bất kỳ giá trị nào (nó được đóng gói tốt và tăng dần). Ví dụ: giá trị enum có thể là "First = 0x1000" hoặc "PageNotFound = 404". NUM_OF_ENUMS sẽ luôn lớn hơn một giá trị lớn nhất được xác định, vì vậy 0x1001 hoặc 405 trong các ví dụ của tôi.
Aku

2

Nếu bạn muốn liên kết các giá trị chuỗi với enum của mình, các phương thức này không hoạt động. Để có một hàm chung, bạn có thể làm:

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

Nó hoạt động vì TypeScript sẽ thêm các khóa trong bước đầu tiên và các giá trị trong bước thứ hai.

Trong TypeScript đó là:

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

Câu trả lời của joe khiến tôi nhận ra rằng việc dựa vào N phím số đầu tiên dễ dàng hơn nhiều so với việc thực hiện các phép thử phức tạp hơ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
Đây là một giả định nguy hiểm vì có thể xác định các giá trị được gán cho enum và chúng không cần phải tăng dần và được đóng gói tốt. Nó không phải là hiếm gặp mặt nạ bit trong một enum ví dụ, hoặc có lẽ một bảng mã lỗi HTML mà bắt đầu từ 400.
Aku


0

cho nodejs:

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

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

0

Lặp lại trên một enum

Chuỗi Enums được sử dụng tốt nhất cho việc này. Đây là một ví dụ:

// 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" 
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.