自动字符串枚举

tru*_*ru7 9 enums typescript

如下所示的字符串枚举看起来很多余,如果您键入某些错误以创建重复项,则代码会很繁琐并且容易出错(请参见以下示例中的最后一个)

enum Cmd{
    StartServer = "StartServer",
    StopServer = "StopServer",
    ResumServer1 = "ResumeServer1",
    ResumServer2 = "ResumeServer1"   // this would cause problems
}
Run Code Online (Sandbox Code Playgroud)

我正在寻找一种只声明枚举元素并自动将值作为符号名称和可选的前缀的方法

我到达的最远的地方是这样的:

export function stringifyEnum(enu:any, prefix:string=undefined){   

  Object.keys(enu).forEach( k =>{
    if (isNumber(k))
        enu[k] = prefix+enu[k]
    else
        enu[k] = prefix+k
  })
}
Run Code Online (Sandbox Code Playgroud)

似乎可行:

enum Cmd{
    StartServer,
    StopServer,
    ResumeServer1,
    ResumeServer2
}

stringifyEnum(Cmd,"Cmd")
console.log(Cmd.StartServer)  // --> CmdStartServer
Run Code Online (Sandbox Code Playgroud)

到目前为止,使用这种方法,唯一的问题是打字稿认为枚举是数字的并且在某些情况下会抱怨。

有没有更好的方法(这里的主要目标是简洁的枚举),或者您认为这种想法有隐患吗?

Rob*_*sen 6

我几乎放弃了 TypeScript 中的枚举,转而使用字符串文字联合类型

对于您的示例,它看起来像:

type Cmd = 'StartServer' | 'StopServer' | 'ResumeServer1' | 'ResumeServer2';
Run Code Online (Sandbox Code Playgroud)

这种方法将在编译时检查中为您提供与枚举相同的好处:

function foo(cmd: Cmd) {}

foo('StartServer'); // OK
foo('BeginServer'); // error
Run Code Online (Sandbox Code Playgroud)

  • 是的,这是需要考虑的事情。我想念的是能够在枚举中找到对特定元素的引用(据我所知,你找不到对特定联合元素的引用) (2认同)

jca*_*alz 6

我认为您不能以编程方式创建前缀以使类型系统理解该前缀。这将需要一些尚不属于该语言的类型操作。有一个现有的建议使用此功能,但它并不像有人正在研究它。


至于使键和值完全相同,您可以编写一个函数,该函数接受字符串列表并生成一个强类型的类似于“枚举”的映射:

function enumize<K extends string>(...args: K[]): {[P in K]: P} {
  const ret = {} as {[P in K]: P};
  args.forEach(k => ret[k]=k);
  return ret;
}
const Cmd = enumize("StartServer", "StopServer", "ResumeServer1", "ResumeServer2");
Run Code Online (Sandbox Code Playgroud)

可以识别出该值Cmd具有类型{ StartServer: "StartServer"; ... }。您将能够按预期访问元素:

console.log(Cmd.StartServer); // works
Run Code Online (Sandbox Code Playgroud)

官员enum还会创建一些命名类型,而enumize()不会。要完全复制类型,您将需要做更多的工作:

type Cmd = keyof typeof Cmd; // allows you to refer to the type Cmd as before
Run Code Online (Sandbox Code Playgroud)

这就是@RobbyCornelissen的答案中提到的联合类型。如果要引用Cmd为类型,则需要它,例如:

declare function requireCmd(cmd: Cmd); // the Cmd here is a type
requireCmd(Cmd.StopServer); // works
Run Code Online (Sandbox Code Playgroud)

如果需要引用每个枚举元素的类型,则必须做更多的工作,包括难看的代码重复:

namespace Cmd {
  export type StartServer = typeof Cmd.StartServer
  export type StopServer = typeof Cmd.StopServer
  export type ResumeServer1 = typeof Cmd.ResumeServer1
  export type ResumeServer2 = typeof Cmd.ResumeServer2
}
Run Code Online (Sandbox Code Playgroud)

这些东西需要引用如下类型Cmd.StopServer

interface CommandInfo {
  kind: Cmd;
}

interface StopServerInfo extends CommandInfo {
   kind: Cmd.StopServer;  // need namespace for this line
   commandIssueDate: Date;
   numberOfUsersForcedOffline: number;
}
Run Code Online (Sandbox Code Playgroud)

但是,如果您不打算做太多事情,那么namespace可以省去一些事情……您可以始终使用该类型typeof Cmd.StopServer


希望能有所帮助;祝好运。