如下所示的字符串枚举看起来非常多余,代码繁琐且容易出错,如果您输错了某些副本(请参阅以下示例中的最后一个)
enum Cmd{
StartServer = "StartServer",
StopServer = "StopServer",
ResumServer1 = "ResumeServer1",
ResumServer2 = "ResumeServer1" // this would cause problems
}
我正在寻找一种方法来只声明枚举的元素,并让值自动成为符号名称,并可选择带前缀
我到达的最远的地方是这样的:
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
})
}
似乎有效:
enum Cmd{
StartServer,
StopServer,
ResumeServer1,
ResumeServer2
}
stringifyEnum(Cmd,"Cmd")
console.log(Cmd.StartServer) // --> CmdStartServer
使用这种方法,到目前为止,唯一的问题是打字稿认为枚举是数字的并且在某些情况下会抱怨。
有没有更好的方法(这里的主要目标是简洁的枚举)或者您是否看到这个想法隐藏的风险?
答案 0 :(得分:3)
我认为您不能以编程方式执行前缀,以便类型系统理解它;这将需要一些不属于该语言的type operations。此功能有一个existing suggestion,但看起来并没有任何人正在使用它。
至于获取键和值完全相同,您可以编写一个函数,该函数接受一个字符串列表并生成一个强类型的“枚举”类似的映射:
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");
我们认识到值Cmd
的类型为{ StartServer: "StartServer"; ... }
。您将能够按预期访问元素:
console.log(Cmd.StartServer); // works
官方enum
还会创建一些名为的类型,而enumize()
则不会。要完全复制类型,您需要做更多的工作:
type Cmd = keyof typeof Cmd; // allows you to refer to the type Cmd as before
这是@ RobbyCornelissen的回答中提到的联合类型。如果您要将Cmd
称为类型,则需要它,如:
declare function requireCmd(cmd: Cmd); // the Cmd here is a type
requireCmd(Cmd.StopServer); // works
如果你需要引用每个枚举元素的类型,你必须做更多繁忙的工作,包括丑陋的代码重复:
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
}
需要那些东西来引用像Cmd.StopServer
这样的类型,如:
interface CommandInfo {
kind: Cmd;
}
interface StopServerInfo extends CommandInfo {
kind: Cmd.StopServer; // need namespace for this line
commandIssueDate: Date;
numberOfUsersForcedOffline: number;
}
但是如果你不打算这么做,那么你可以省略namespace
的东西......你总是可以使用typeof Cmd.StopServer
类型。
希望有所帮助;祝你好运。
答案 1 :(得分:1)
我几乎放弃了TypeScript中的枚举,转而使用string literal union types。
对于您的示例,如下所示:
type Cmd = 'StartServer' | 'StopServer' | 'ResumeServer1' | 'ResumeServer2';
这种方法在编译时检查中会给你带来与枚举相同的好处:
function foo(cmd: Cmd) {}
foo('StartServer'); // OK
foo('BeginServer'); // error
答案 2 :(得分:0)
如果您正在寻找一种方法来实现多个键可以映射到相同值的方案,那么地图可以帮助您实现目标。你可以冻结它以确保它是不可变的:
const CmdMap = {
StartServer: "StartServer",
StopServer: "StopServer",
ResumServer1: "ResumeServer1",
ResumServer2: "ResumeServer1"
};
Object.freeze(CmdMap);
如果您想要有前缀命令,请从工厂获取地图:
function getCommandMap(prefix: string) {
const cmdMap = {
StartServer: `${prefix}StartServer`,
StopServer: `${prefix}StopServer`,
ResumServer1: `${prefix}ResumeServer1`,
ResumServer2: `${prefix}ResumeServer1`
};
Object.freeze(cmdMap);
return cmdMap;
}
const CmdMap = getCommandMap('Cmd');
console.log(CmdMap.StartServer);