地图打字稿枚举

时间:2016-12-23 21:36:35

标签: javascript typescript enums

我如何映射打字稿枚举?例如,使用字符串可以执行此操作:

let arr = [ 'Hello', 'Goodbye' ];

arr.map(v => {
  if (v === 'Hello') {
    return ':)';
  } else if (v === 'Goodbye') {
    return ':(';
  }
); // [ ':)', ':(' ]

当然,这不适用于枚举:

enum MyEnum { Hello, Goodbye };

MyEnum.map(v => {
  if (v === MyEnum.Hello) {
    return ':)';
  } else if (v === MyEnum.Goodbye) {
    return ':(';
  }
}); // does not work

理想情况下,我想以一种通用方式执行此操作,因此我可以简单地使用任何枚举,并通过地图函数将其保留,同时保留类型信息。用法可能如下所示:

map(MyEnum, v => {
  if (v === MyEnum.Hello) {
    return ':)';
  } else if (v === MyEnum.Goodbye) {
    return ':(';
  }
}); // [ ':)', ':(' ]

我一直在努力寻找能够为我做这个功能的功能,但仍然存在让generics恰到好处的问题。

4 个答案:

答案 0 :(得分:5)

解决这个问题的功能非常简单。

// you can't use "enum" as a type, so use this.
type EnumType = { [s: number]: string };

function mapEnum (enumerable: EnumType, fn: Function): any[] {
    // get all the members of the enum
    let enumMembers: any[] = Object.keys(enumerable).map(key => enumerable[key]);

    // we are only interested in the numeric identifiers as these represent the values
    let enumValues: number[] = enumMembers.filter(v => typeof v === "number");

    // now map through the enum values
    return enumValues.map(m => fn(m));
}

正如您所看到的,我们首先需要获取枚举的所有键(MyEnum.Hello在运行时实际上是1)然后只是映射它们,传递函数。

使用它也很简单(与你的例子相同,虽然我更改了名称):

enum MyEnum { Hello, Goodbye };

let results = mapEnum(MyEnum, v => {
  if (v === MyEnum.Hello) {
    return ':)';
  } else if (v === MyEnum.Goodbye) {
    return ':(';
  }
});

console.log(results); // [ ':)', ':(' ]

我们需要将枚举过滤为数字的原因是因为编译枚举的方式。

你的枚举实际编译为:

var MyEnum;
(function (MyEnum) {
    MyEnum[MyEnum["Hello"] = 0] = "Hello";
    MyEnum[MyEnum["Goodbye"] = 1] = "Goodbye";
})(MyEnum || (MyEnum = {}));
;

但我们对"Hello""Goodbye"不感兴趣,因为我们无法在运行时使用这些内容。

在函数之前,您还会注意到一个有趣的type语句。这是因为您无法将参数键入someParameter: enum,您需要将其明确说明为number -> string地图。

答案 1 :(得分:2)

要映射枚举,请执行以下操作:

(Object.keys(MyEnum) as Array<keyof typeof MyEnum>).map((key) => {})

答案 2 :(得分:1)

使用ts-enum-utilnpmgithub),它很简单,类型安全(使用泛型),并负责为您跳过数字反向查找条目:

import { $enum } from "ts-enum-util";

enum MyEnum { Hello, Goodbye };

$enum(MyEnum).map(v => {
    if (v === MyEnum.Hello) {
        return ':)';
    } else if (v === MyEnum.Goodbye) {
        return ':(';
    }
}); // produces [':(', ':)']

注意:ts-enum-util总是根据排序的枚举键的顺序进行迭代,以保证所有环境中的顺序一致。 Object.keys()没有保证的顺序,因此不可能以跨平台保证的方式“按照它们被定义的顺序”迭代枚举。 (更新:ts-enum-util的新版本现在保留了定义枚举的原始顺序)

如果您使用字符串枚举,则将其与ts-string-visitornpmgithub)结合使用,以进行更通用的类型安全编译器检查,以保证您处理所有可能的枚举值在你的地图功能: (更新:ts-enum-util的新版本现在包含ts-string-visitor的功能,现在它也适用于数字枚举!)

import { $enum } from "ts-enum-util";
import { mapString } from "ts-string-visitor";

enum MyEnum { Hello = "HELLO", Goodbye = "GOODBYE" };

$enum(MyEnum).map(v => {
    // compiler error if you forget to handle a value, or if you
    // refactor the enum to have different values, etc.
    return mapString(v).with({
        [MyEnum.Hello]: ':)',
        [MyEnum.Goodby]: ':('
    });
}); // produces [':(', ':)']

答案 3 :(得分:0)

我不会称之为一般,但我多次使用它,也可能对其他人也很方便:

type TMyEnum = ':)'|':(';
class MyEnum {
    static Hello: TMyEnum = ':)';
    static Goodbye: TMyEnum = ':(';
}
console.log(MyEnum.Hello); // :)
console.log(MyEnum.Goodbye); // :(

现在你不需要任何映射函数,它按预期工作,但你必须为每个枚举创建单独的类似的类(这应该不是一个问题,因为你无论如何都会这样做)。我现在能想到的唯一缺点就是你不能迭代它的属性。但到目前为止,这对我来说不是问题,我不需要它。您可以在需要时向类中添加静态数组。