【问题标题】:Is it possible to write a generic enum-member-name getter?是否可以编写一个通用的 enum-member-name getter?
【发布时间】:2021-02-27 02:24:51
【问题描述】:

我想编写一个函数来获取传递给它的枚举成员的名称。比如:

enum Cars {
   dodge,
   ford,
   toyota
}
enum Trucks {
   ford,
   nissan,
   gmc
}
class Foo() {
  truckType : Trucks;
}
const getEnumMemberName = function(enumMember : any) : string {
    //implementation?
}


const main = function() {
   let f = new Foo();
   foo.truckType = Trucks.ford;
   console.log(`Trucktype is ${getEnumMemberName(foo.truckType}`);
}
 
main();
//Should log "Trucktype is ford" to the console

我认为如果您不将枚举类型本身提供给函数,这可能是不可能的,因为虽然 foo.truckType 被键入为卡车,但看起来所有字段在运行时都是一个没有类型信息的数字可用。

我知道 a) 我可以将枚举定义为字符串值而不是默认数字,而且我还知道如果我知道使用 let name = Trucks[foo.trucktype] 的枚举名称,我可以获取成员的名称。我的问题是您是否可以从类型为枚举的变量(例如truckType : Trucks)推断枚举类型,还是只是转换为数字并且类型信息不可用?

【问题讨论】:

  • TS编译成JS时类型系统为erasedenum values 只是运行时的字符串或数字;没有返回枚举对象的“链接”。另一方面,数字枚举 object 具有 reverse mappings,您可以使用它从值中获取名称。但是你不能在不传入枚举对象的情况下对任意枚举执行此操作,例如getEnumMemberName(Trucks, foo.truckType)。如果这对你有用,我可以把它写下来作为答案。
  • 是的,这就是我的怀疑。我希望有一个 getType 函数,我可以用它来确定参数的类型。
  • 那么,您是否想要一个答案(例如,“不,抱歉”)以及像 this 这样的代码作为可能的解决方法?
  • 可能会有所帮助,是的,谢谢。主要是我不明白 Trucks 参数的类型是什么。
  • 您提供的将枚举类型作为记录传递的代码非常有用,谢谢。如果你想发布它,我会接受它。

标签: typescript


【解决方案1】:

TypeScript 中的大多数功能是:

  • 来自一些 JavaScript 版本(例如,class 是 ES2015+)并根据目标 JS 版本是否支持该功能按原样或 downleveled 发出;或
  • 纯粹基于类型系统(例如,类型注释,interfaces)并且完全来自发出的 JavaScript 的 erased

但是enums 有点奇怪,因为它们既没有被删除也没有按原样发出。在发出的 JavaScript 中,enum 对象(例如,CarsTrucks)只是一个普通的旧 JavaScript 对象 将键映射到值。而这些enum 值(例如Cars.dodgeTrucks.gmc)只是数字或字符串。

您的 CarsTrucks 枚举以类似于以下 JavaScript 的形式发出:

var Cars;
(function (Cars) {
    Cars[Cars["dodge"] = 0] = "dodge";
    Cars[Cars["ford"] = 1] = "ford";
    Cars[Cars["toyota"] = 2] = "toyota";
})(Cars || (Cars = {}));

var Trucks;
(function (Trucks) {
    Trucks[Trucks["ford"] = 0] = "ford";
    Trucks[Trucks["nissan"] = 1] = "nissan";
    Trucks[Trucks["gmc"] = 2] = "gmc";
})(Trucks || (Trucks = {}));

这有点令人费解,但您可以验证Cars.dodge 在运行时的值只是0 的数字,Trucks.ford 在运行时的值也是如此。


所以,如果你问是否有可能实现 getEnumMemberName() 这样 getEnumMemberName(Cars.dodge)==="dodge"getEnumMemberName(Trucks.ford)==="ford"",很遗憾,答案是 。在运行时,Cars.dodge 和 @987654346 @ 只是 0,因此 getEnumMemberName(0) 必须同时生成 "dodge""ford",具体取决于它无法获得的信息。


如果你愿意写getEnumMemberName(),让它接受两个参数,额外的一个是枚举object,那么可以这样写,因为缺少枚举映射信息会现在可以使用了。

由于你的 CarsTrucks 枚举是 numeric,TypeScript 已经为你提供了一个 reverse mapping:例如,如果是 Cars.dodge === 0,那么 Cars[0] === "dodge"。 (这就是上面“复杂”的代码正在做的事情。)

如果你只需要支持数字枚举,那么getEnumMemberName()可以通过简单的对象索引来实现:

const getEnumMemberName = function (enumObj: Record<number, string>, enumMember: number): string {
  return enumObj[enumMember];
}

数字枚举对象被认为可以分配给Record&lt;number, string&gt;:这意味着number 键应该产生string 值,这是由于反向映射。然后以下代码按预期工作:

  let foo = new Foo();
  foo.truckType = Trucks.ford;
  console.log(`Trucktype is ${getEnumMemberName(Trucks, foo.truckType)}`); 
  // Trucktype is ford

如果您想编写 getEnumMemberName() 并让它适用于任意枚举(不仅是 number 还适用于 string 枚举)并且您没有遇到多个枚举键具有相同值的情况,您可以搜索枚举对象而不是依赖反向映射:

  const getEnumMemberName = function <T>(enumObj: T, enumMember: T[keyof T]): keyof T {
    const key = (Object.keys(enumObj) as Array<keyof T>).find(k => enumObj[k] === enumMember);
    if (typeof key === "undefined") throw new Error("Not an enum member");
    return key;
  }

Playground link to code

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-11
    • 2011-10-29
    • 1970-01-01
    • 2014-11-14
    • 1970-01-01
    • 1970-01-01
    • 2018-11-01
    • 1970-01-01
    相关资源
    最近更新 更多