網站搜尋

javascript - 將 TypeScript 枚舉轉換為物件陣列


我有一個這樣定義的枚舉:

export enum GoalProgressMeasurements {
    Percentage = 1,
    Numeric_Target = 2,
    Completed_Tasks = 3,
    Average_Milestone_Progress = 4,
    Not_Measured = 5
}

然而,我希望它能以我們 API 的物件陣列/列表形式表示,如下所示:

[{id: 1, name: 'Percentage'}, 
 {id: 2, name: 'Numeric Target'},
 {id: 3, name: 'Completed Tasks'},
 {id: 4, name: 'Average Milestone Progress'},
 {id: 5, name: 'Not Measured'}]

有沒有一個簡單且符合原生語法的方法來做到這件事,或者我必須建立一個函數,將列舉型別轉換成整數和字串,然後把物件建立成陣列?

答案#1

如果你正在使用 ES8

僅在這種情況下,它將運作得非常好。它會給你所提供的 enum 的值陣列。

enum Colors {
  WHITE = 0,
  BLACK = 1,
  BLUE = 3
}

const colorValueArray = Object.values(Colors); //[ 'WHITE', 'BLACK', 'BLUE', 0, 1, 3 ]

你將會得到像這樣的 colorValueArray:[ 'WHITE', 'BLACK', 'BLUE', 0, 1, 3 ]。所有的鍵會在陣列的前半部分,而所有的值會在後半部分。

甚至這種枚舉也能正常運作

enum Operation {
    READ,
    WRITE,
    EXECUTE
}

但這個解決方案對像這樣的異質枚舉不起作用

enum BooleanLikeHeterogeneousEnum {
  No = 0,
  Yes = "YES",
}

答案#2

一個棘手的部分是 TypeScript 會在生成的物件中對枚舉進行「雙重」映射,因此它可以通過鍵和值來訪問。

enum MyEnum {
    Part1 = 0,
    Part2 = 1
}

將被發出為

{
   Part1: 0,
   Part2: 1,
   0: 'Part1',
   1: 'Part2'
}

所以你應該先過濾物件再進行映射。所以 @Diullei 的解法是正確的答案。這是我的實作:

// Helper
const StringIsNumber = value => isNaN(Number(value)) === false;

// Turn enum into array
function ToArray(enumme) {
    return Object.keys(enumme)
        .filter(StringIsNumber)
        .map(key => enumme[key]);
}

像這樣使用它:

export enum GoalProgressMeasurements {
    Percentage,
    Numeric_Target,
    Completed_Tasks,
    Average_Milestone_Progress,
    Not_Measured
}

console.log(ToArray(GoalProgressMeasurements));

答案#3

簡單來說,這將返回一個枚舉值的陣列:

 Object.values(myEnum);

答案#4

列舉是存在於運行時的真實物件。因此,你可以通過如下方式反向映射:

let value = GoalProgressMeasurements.Not_Measured;
console.log(GoalProgressMeasurements[value]);
// => Not_Measured

根據這個,你可以使用以下程式碼:

export enum GoalProgressMeasurements {
    Percentage = 1,
    Numeric_Target = 2,
    Completed_Tasks = 3,
    Average_Milestone_Progress = 4,
    Not_Measured = 5
}

let map: {id: number; name: string}[] = [];

for(var n in GoalProgressMeasurements) {
    if (typeof GoalProgressMeasurements[n] === 'number') {
        map.push({id: <any>GoalProgressMeasurements[n], name: n});
    }
}

console.log(map);

參考資料: https://www.typescriptlang.org/docs/handbook/enums.html

答案#5

簡單解決方案。您可以使用以下函數將您的 Enum 轉換為物件陣列。

 buildGoalProgressMeasurementsArray(): Object[] {

    return Object.keys(GoalProgressMeasurements)
              .map(key => ({ id: GoalProgressMeasurements[key], name: key }))
 }

如果你需要去掉那個底線,我們可以使用正則表達式如下:

buildGoalProgressMeasurementsArray(): Object[] {

    return Object.keys(GoalProgressMeasurements)
              .map(key => ({ id: GoalProgressMeasurements[key], name: key.replace(/_/g, ' ') }))
 }

答案#6

我使用

Object.entries(GoalProgressMeasurement).filter(e => !isNaN(e[0]as any)).map(e => ({ name: e[1], id: e[0] }));

一行簡單的程式碼就能完成工作。

它通過三個簡單的步驟完成工作
- 使用 Object.entries 載入鍵和值的組合。
- 過濾掉非數字(因為 TypeScript 會生成反向查找的值)。
- 然後我們將其映射到我們喜歡的陣列對象。

答案#7

多虧了 polkovnikov.ph,我終於能找到一個適用於大多數使用情境的解決方案。

該問題的有效解答

type Descripted<T> = {
    [K in keyof T]: {
        readonly id: T[K];
        readonly description: string;
    }
}[keyof T]

/**
 * Helper to produce an array of enum descriptors.
 * @param enumeration Enumeration object.
 * @param separatorRegex Regex that would catch the separator in your enum key.
 */
export function enumToDescriptedArray<T>(enumeration: T, separatorRegex: RegExp = /_/g): Descripted<T>[] {
    return (Object.keys(enumeration) as Array<keyof T>)
        .filter(key => isNaN(Number(key)))
        .filter(key => typeof enumeration[key] === "number" || typeof enumeration[key] === "string")
        .map(key => ({
            id: enumeration[key],
            description: String(key).replace(separatorRegex, ' '),
        }));
}

範例:

export enum GoalProgressMeasurements {
    Percentage = 1,
    Numeric_Target = 2,
    Completed_Tasks = 3,
    Average_Milestone_Progress = 4,
    Not_Measured = 5
}

console.log(enumToDescriptedArray(GoalProgressMeasurements))
// Produces:
/*
[
    {id: 1, description: "Percentage"},
    {id: 2, description: "Numeric Target"},
    {id: 3, description: "Completed Tasks"},
    {id: 4, description: "Average Milestone Progress"},
    {id: 5, description: "Not Measured"}
]
*/

另外,我有一個有用的工具函數,用來將列舉對象映射到它所擁有的可用值數組:

繪圖者

type NonFunctional<T> = T extends Function ? never : T;

/**
 * Helper to produce an array of enum values.
 * @param enumeration Enumeration object.
 */
export function enumToArray<T>(enumeration: T): NonFunctional<T[keyof T]>[] {
    return Object.keys(enumeration)
        .filter(key => isNaN(Number(key)))
        .map(key => enumeration[key])
        .filter(val => typeof val === "number" || typeof val === "string");
}

實際應用案例

  • 數字列舉
enum Colors1 {
    WHITE = 0,
    BLACK = 1
}
console.log(Object.values(Colors1)); // ['WHITE', 'BLACK', 0, 1]
console.log(enumToArray(Colors1));   // [0, 1]
  • 字串列舉
enum Colors2 {
    WHITE = "white",
    BLACK = "black"
}
console.log(Object.values(Colors2)); // ['white', 'black']
console.log(enumToArray(Colors2));   // ['white', 'black']
  • 異質列舉
enum Colors4 {
    WHITE = "white",
    BLACK = 0
}
console.log(Object.values(Colors4)); // ["BLACK", "white", 0]
console.log(enumToArray(Colors4));   // ["white", 0]
  • 列舉與具有導出函數的命名空間合併
enum Colors3 {
    WHITE = "white",
    BLACK = "black"
}
namespace Colors3 {
    export function fun() {}
}
console.log(Object.values(Colors3)); // ['white', 'black', Function]
console.log(enumToArray(Colors3));   // ['white', 'black']

答案#8

class EnumHelpers {

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

    static getNames(e: any) {
        return EnumHelpers.getObjValues(e).filter(v => typeof v === 'string') as string[];
    }

    static getValues<T extends number>(e: any) {
        return EnumHelpers.getObjValues(e).filter(v => typeof v === 'number') as T[];
    }

    static getSelectList<T extends number, U>(e: any, stringConverter: (arg: U) => string) {
        const selectList = new Map<T, string>();
        this.getValues(e).forEach(val => selectList.set(val as T, stringConverter(val as unknown as U)));
        return selectList;
    }

    static getSelectListAsArray<T extends number, U>(e: any, stringConverter: (arg: U) => string) {
        return Array.from(this.getSelectList(e, stringConverter), value => ({ value: value[0] as T, presentation: value[1] }));
    }

    private static getObjValues(e: any): (number | string)[] {
        return Object.keys(e).map(k => e[k]);
    }
}

答案#9

在陣列中取得列舉值的範例:

export enum DocumentationTypeEnum {
  GDPR = 'GDPR',
  HELP = 'HELP',
  OTHER = 'OTHER',
  FOOTER = 'FOOTER'
}
const keys = Object.keys(DocumentationTypeEnum);
    
console.log(keys); // Output :  ["GDPR", "HELP", "OTHER", "FOOTER"]

答案#10

我不喜歡上述任何答案,因為它們都無法正確處理 TypeScript 列舉中可以作為值的字串/數字混合情況。

以下函數遵循 TypeScript 列舉的語義,提供一個正確的鍵對值映射。由此,獲取對象數組、僅鍵或僅值都是簡單的。

/**
 * Converts the given enum to a map of the keys to the values.
 * @param enumeration The enum to convert to a map.
 */
function enumToMap(enumeration: any): Map<string, string | number> {
  const map = new Map<string, string | number>();
  for (let key in enumeration) {
      //TypeScript does not allow enum keys to be numeric
      if (!isNaN(Number(key))) continue;

      const val = enumeration[key] as string | number;

      //TypeScript does not allow enum value to be null or undefined
      if (val !== undefined && val !== null)
          map.set(key, val);
  }

  return map;
}

範例用法:

enum Dog {
    Rover = 1,
    Lassie = "Collie",
    Fido = 3,
    Cody = "Mutt",
}

let map = enumToMap(Dog); //Map of keys to values

let objs = Array.from(map.entries()).map(m => ({id: m[1], name: m[0]})); //Objects as asked for in OP
let entries = Array.from(map.entries()); //Array of each entry
let keys = Array.from(map.keys()); //An array of keys
let values = Array.from(map.values()); //An array of values

我也要指出,發帖者對枚舉的理解是顛倒的。枚舉中的「鍵」技術上是在左側,而值則在右側。TypeScript 允許你在右側重複值,無論你想重複多少次。