首页 文章

Map 打字稿枚举

提问于
浏览
9

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

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 函数 while preserving type information . 用法可能如下所示:

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

我一直在努力为我提供一个能够为我做这个功能的功能但是仍然遇到让generics恰到好处的问题 .

3 回答

  • 1

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

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

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

  • 4

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

    // 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 Map .

  • 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()没有保证顺序,因此无法以跨平台保证的方式迭代枚举"in the order they were defined" .

    如果您正在使用字符串枚举,则将其与 ts-string-visitornpmgithub)组合以进行更通用的类型安全编译器检查,以保证您在map函数中处理所有可能的枚举值:

    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 [':(', ':)']
    

相关问题