我如何映射打字稿枚举?例如,使用字符串可以执行此操作:
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恰到好处的问题。
答案 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-util
(npm,github),它很简单,类型安全(使用泛型),并负责为您跳过数字反向查找条目:
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-visitor
(npm,github)结合使用,以进行更通用的类型安全编译器检查,以保证您处理所有可能的枚举值在你的地图功能:
(更新: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); // :(
现在你不需要任何映射函数,它按预期工作,但你必须为每个枚举创建单独的类似的类(这应该不是一个问题,因为你无论如何都会这样做)。我现在能想到的唯一缺点就是你不能迭代它的属性。但到目前为止,这对我来说不是问题,我不需要它。您可以在需要时向类中添加静态数组。