繁体   English   中英

Map Typescript 枚举

[英]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-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 )结合使用,以进行更通用的类型安全编译器检查,以保证您在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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM