
[英]How can I declare an Enum in TypeScript that will map to environment-defined global properties?
[英]Map Typescript Enum
我将如何 map 枚举 typescript ? 例如,对于字符串,您可以这样做:
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 function 中,同时保留类型信息。 用法可能看起来像这样:
map(MyEnum, v => {
if (v === MyEnum.Hello) {
return ':)';
} else if (v === MyEnum.Goodbye) {
return ':(';
}
}); // [ ':)', ':(' ]
我一直在摆弄一个 function 来为我做这件事,但一直有问题让 generics 恰到好处。
要映射枚举,请执行以下操作:
(Object.keys(MyEnum) as Array<keyof typeof MyEnum>).map((key) => {})
解决这个问题的功能非常简单。
// 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
映射。
使用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 )结合使用,以进行更通用的类型安全编译器检查,以保证您在map函数中处理所有可能的枚举值:(更新:新版本的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 [':(', ':)']
也许这会帮助你:
enum NumericEnums {
'PARAM1' = 1,
'PARAM2',
'PARAM3',
}
enum HeterogeneousEnums {
PARAM1 = 'First',
PARAM2 = 'Second',
PARAM3 = 3,
}
type EnumType = { [key: string]: string | number };
type EnumAsArrayType = {
key: string;
value: string | number;
}[];
const enumToArray = (data: EnumType): EnumAsArrayType =>
Object.keys(data)
.filter((key) => Number.isNaN(+key))
.map((key: string) => ({
key,
value: data[key],
}));
console.log(enumToArray(NumericEnums));
console.log(enumToArray(HeterogeneousEnums));
// Usage
enumToArray(HeterogeneousEnums).map(({ key, value }) => {
console.log(`${key}: ${value}`);
// Your necessary logic
return null;
});
我不会称它为一般但我多次使用它也可能对其他人也很方便:
type TMyEnum = ':)'|':(';
class MyEnum {
static Hello: TMyEnum = ':)';
static Goodbye: TMyEnum = ':(';
}
console.log(MyEnum.Hello); // :)
console.log(MyEnum.Goodbye); // :(
现在你不需要任何映射函数,它按预期工作,但你必须为每个枚举创建单独的类似的类(这应该不是一个问题,因为你无论如何都会这样做)。 我现在能想到的唯一缺点就是你不能迭代它的属性。 但到目前为止,这对我来说不是问题,我不需要它。 您可以在需要时向类中添加静态数组。
这是您可以使用的工作功能。 下面我将ItemMaterial
传递给getEnumKeys
函数并获得 ["YELLOW", "WHITE", "ROSE", "BLACK"]。
同样使用getEnumValues
函数来获取枚举的值。
看看splitEnumKeysAndValues
函数,看看这些变量是如何从枚举中提取的。
enum ItemMaterial {
YELLOW,
WHITE,
ROSE,
BLACK,
}
const keys = getEnumKeys<typeof ItemMaterial>(ItemMaterial)
const values = getEnumValues<typeof ItemMaterial, `${ItemMaterial}`>(ItemMaterial);
function getEnumKeys<TypeofEnum>(value: TypeofEnum): keyof TypeofEnum {
const { values, keys } = splitEnumKeysAndValues(value);
return keys as unknown as keyof TypeofEnum;
}
function getEnumValues<TypeofEnum, PossibleValues>(value: TypeofEnum): PossibleValues[] {
const { values, keys } = splitEnumKeysAndValues(value);
return values as unknown as PossibleValues[];
}
function splitEnumKeysAndValues<T>(value: T): { keys: keyof T, values: Array<string | number> } {
const enumKeys = Object.keys(value);
const indexToSplit = enumKeys.length / 2
const enumKeysKeyNames = enumKeys.slice(0, indexToSplit) as unknown as keyof T;
const enumKeysKeyValues = enumKeys.slice(indexToSplit);
return {
keys: enumKeysKeyNames,
values: enumKeysKeyValues,
}
}
Typescript 中的映射对于编写更少的代码来说非常强大。 我最近一直在使用键值枚举映射,并且会推荐它! 这里有几个例子!
基本枚举用法
enum InlineStyle {
"Bold",
"Italic",
"Underline"
}
type IS = keyof typeof InlineStyle
// Example of looping
Object.keys(InlineStyle) as Array<IS>).forEach((key) => {
// code here
})
// Example of calling a function
const styleInline = (style: IS) => {
// code here
}
枚举键值用法
enum ListStyle {
"UL" = "List",
"OL" = "Bullet points"
}
// Example of looping
Object.entries(ListStyle).forEach(([key, value]) => {
// code here
})
接口映射
enum InlineStyle {
"Bold" = "isBold",
"Italic" = "isItalic",
"Underline" = "isUnderlined"
}
type InlineStyleType = Record<InlineStyle, boolean>
enum ListStyle {
"UL",
"OL"
}
type LS keyof typeof ListStyle
interface HTMLBlock extends InlineStyleType {
// This has extended with
// isBold: boolean
// isItalic: boolean
// isUnderlined: boolean
listType: LS
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.