A string enum like the following looks quite redundant, tedious to code and prone to errors if you mistype some creating a duplicate (see as in the last one in the following sample)
enum Cmd{
StartServer = "StartServer",
StopServer = "StopServer",
ResumServer1 = "ResumeServer1",
ResumServer2 = "ResumeServer1" // this would cause problems
}
I am looking for a way to declare only the elements of the enum and have the values automatically be the symbol name and optionally with a prefix
The farthest I arrived is to something like this:
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
})
}
that seems to work:
enum Cmd{
StartServer,
StopServer,
ResumeServer1,
ResumeServer2
}
stringifyEnum(Cmd,"Cmd")
console.log(Cmd.StartServer) // --> CmdStartServer
With this approach, so far, the only problem is that typescript considers the enum being numerical and complains in some cases.
Is there a better approach to this (the main target here is a concise enumeration) or do you see hidden risks in this idea?
Note: you cannot set an enum to string as enums can only have integers. The conversion above simply converts an already existing enum into a string. It is set that every enumeration begins at 0.
Enumerations are created using classes. Enums have names and values associated with them. Properties of enum: Enums can be displayed as string or repr.
Here we will see how to map some enum type data to a string in C++. There is no such direct function to do so. But we can create our own function to convert enum to string. We shall create a function that takes an enum value as the argument, and we manually return the enum names as a string from that function.
In TypeScript, enums, or enumerated types, are data structures of constant length that hold a set of constant values. Each of these constant values is known as a member of the enum. Enums are useful when setting properties or values that can only be a certain number of possible values.
I have all but abandoned enums in TypeScript in favor of string literal union types.
For your example that would look like:
type Cmd = 'StartServer' | 'StopServer' | 'ResumeServer1' | 'ResumeServer2';
This approach will give you the same benefits in compile-time checks as an enum:
function foo(cmd: Cmd) {}
foo('StartServer'); // OK
foo('BeginServer'); // error
I don't think you can programmatically do a prefix so that the type system understands it; that would require some type operations that are not part of the language yet. There is an existing suggestion for this feature, but it doesn't look like anyone is working on it.
As for getting the key and the value to be exactly the same, you can write a function that takes a list of strings and produces a strongly-typed "enum"-like map:
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");
It is recognized that the value Cmd
has the type { StartServer: "StartServer"; ... }
. You will be able to access elements as expected:
console.log(Cmd.StartServer); // works
An official enum
also creates some named types, which enumize()
doesn't. To fully replicate the types, you would have to more work:
type Cmd = keyof typeof Cmd; // allows you to refer to the type Cmd as before
That's the union type mentioned in @RobbyCornelissen's answer. You need it if you're going to refer to Cmd
as a type, as in:
declare function requireCmd(cmd: Cmd); // the Cmd here is a type
requireCmd(Cmd.StopServer); // works
If you need to refer to the types of each enum element, you have to do even more busywork, including ugly code duplication:
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
}
That stuff would be needed to refer to types like Cmd.StopServer
as in:
interface CommandInfo {
kind: Cmd;
}
interface StopServerInfo extends CommandInfo {
kind: Cmd.StopServer; // need namespace for this line
commandIssueDate: Date;
numberOfUsersForcedOffline: number;
}
But if you're not going to do that very much, then you can leave out the namespace
stuff... you can always use the type typeof Cmd.StopServer
instead.
Hope that helps; good luck.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With