4 ms·
After trying enums in TS a long time back I also realized it was best to avoid them. Here is my solution to this: // filename: role.ts export const Rol
by joshstrange 5y ago
After trying enums in TS a long time back I also realized it was best to avoid them. Here is my solution to this:
// filename: role.ts
export const Role = {
CUSTOMER: 'customer',
ADMIN: 'admin',
SYSTEM: 'system',
STAFF: 'staff',
} as const;
type TRole = keyof typeof Role;
export type TUserRole = typeof Role[TRole];
Using this structure I can reference any of my roles by `Role.CUSTOMER` and the value is `customer` because it's just a `Record<string, string>` at the end of the day. But I am able to type things by using my `TUserRole` so a function can required that the input be one of the values above. For me this is really clean and easy to use. Note the `type TRole` isn't exported as it's only an intermediary in this process, I could just as well name it `type temp` in all my files and never worry about conflicts.
This way I'm not spreading "special" strings all over my code (always a sign of code smell for me), I can changed everything at a central location, and it's valid JS (it's just an object).
EDIT: I know that `as const` seems unnecessary but I'm pretty sure it's needed for some reason, I whittled this down to the smallest/simplest code block I could and I just copy/paste this pattern whenever I need enum-like functionality.
- conaclos 5y agoYou can also name your type with the same name as the value: export const Role = { CUSTOMER: 'customer', ADMIN: 'admin', SYSTEM: 'system', STAFF: 'staff', } as const export type Role = typeof Role[keyof typeof Role]
- joshstrange 5y ago:facepalm: of course I can, I don't know why it never occurred to me. Probably a case of "if it's ain't broke" but going forward I'll probably switch to this style.
- conaclos 5y agoFor a long time I avoided to use the same name for a type and a value because I was afraid of possible breaking change in the feature. And then I realized that this is an intended feature of TypeScript: type merging. Here the type `Role` merges with the type of the value `Role :)
- conaclos 5y ago`as const` is needed for ensuring that TypeScript uses literal types for the enum members. Otherwise you ends with `TUserRole = string`.
- cypressious 5y agoHave you tried `const enum`? See https://www.typescriptlang.org/docs/handbook/enums.html#const-enums https://www.typescriptlang.org/docs/handbook/enums.html#cons... > Const enums can only use constant enum expressions and unlike regular enums they are completely removed during compilation. Const enum members are inlined at use sites.
- deleted 5y ago[deleted]
- ragnese 5y agoI remember reading somewhere that the TypeScript devs considered const enums to be a mistake and recommend against using them. I don't remember why, though.
- russellsprouts 5y agoconst enums are one of the few cases where type information changes the emitted JS, something that's arguably a bigger problem than TypeScript-specific, but still just syntax sugar, syntax highlighted in the article. Const enums are erased at compile time. If you have a reference to `MyEnum.VAR`, TS has to check whether the enum is a const enum, and if so, replace with something like `1 /* VAR */`. This means that the type information in one file (where the enum is defined) is necessary to determine the proper output of any file that uses it.
- ragnese 5y agoThank you for the answer! I won't waste your time because I'm sure the answer is somewhere on the web, but off the top of my head, I don't understand why TS "has" to emit different JS. I would've assumed that the entire point of a const enum is to inline the raw value in the emitted JS, and thus, the programmer should be careful to remember/know that the code is dealing with raw ints/strings.
- russellsprouts 5y ago
- ragnese 5y agoI disagree. Enums are fine if they're string-only. It's only the numeric enums that cause the issues everyone complains about enums for. If there were a lint for making sure all enums are string-only, it would be the best solution, IMO. AFAIK, your solution is (slightly) worse than an enum in several ways and better in none: export enum Role { CUSTOMER = 'customer', ADMIN = 'admin', SYSTEM = 'system', STAFF = 'staff', } I think that implements everything yours does, but is easier to grok and fewer lines/declarations.