Well, there is a difference between string enums and literal types in the transpiled code.
Compare the Typescript Code
// with a String Literal Type
type MyKeyType1 = 'foo' | 'bar' | 'baz';
// or with a String Enum
enum MyKeyType2 {
FOO = 'foo',
BAR = 'bar',
BAZ = 'baz'
}
With the transpiled JavaScript Code
// or with a String Enum
var MyKeyType2;
(function (MyKeyType2) {
MyKeyType2["FOO"] = "foo";
MyKeyType2["BAR"] = "bar";
MyKeyType2["BAZ"] = "baz";
})(MyKeyType2 || (MyKeyType2 = {}));
What you can see is, there is no generated code for the string literal. Because Typescripts Transpiler is only using for type safety while transpiling. At runtime string literals are "generated to dumb" strings. No references between the definition of the literal and the usages.
So there is a third alternative called const enum
Look at this
// with a String Literal Type
type MyKeyType1 = 'foo' | 'bar' | 'baz';
// or with a String Enum
enum MyKeyType2 {
FOO = 'foo',
BAR = 'bar',
BAZ = 'baz'
}
// or with a Const String Enum
const enum MyKeyType3 {
FOO = 'foo',
BAR = 'bar',
BAZ = 'baz'
}
var a : MyKeyType1 = "bar"
var b: MyKeyType2 = MyKeyType2.BAR
var c: MyKeyType3 = MyKeyType3.BAR
will be transpiled to
// or with a String Enum
var MyKeyType2;
(function (MyKeyType2) {
MyKeyType2["FOO"] = "foo";
MyKeyType2["BAR"] = "bar";
MyKeyType2["BAZ"] = "baz";
})(MyKeyType2 || (MyKeyType2 = {}));
var a = "bar";
var b = MyKeyType2.BAR;
var c = "bar" /* BAR */;
For further playing you can check this link
I prefer the const enum case, because of the convenient way of typing Enum.Value. Typescript will do the rest for me to get the highest performance when transpiling.
Answer from Daniel R on Stack OverflowWell, there is a difference between string enums and literal types in the transpiled code.
Compare the Typescript Code
// with a String Literal Type
type MyKeyType1 = 'foo' | 'bar' | 'baz';
// or with a String Enum
enum MyKeyType2 {
FOO = 'foo',
BAR = 'bar',
BAZ = 'baz'
}
With the transpiled JavaScript Code
// or with a String Enum
var MyKeyType2;
(function (MyKeyType2) {
MyKeyType2["FOO"] = "foo";
MyKeyType2["BAR"] = "bar";
MyKeyType2["BAZ"] = "baz";
})(MyKeyType2 || (MyKeyType2 = {}));
What you can see is, there is no generated code for the string literal. Because Typescripts Transpiler is only using for type safety while transpiling. At runtime string literals are "generated to dumb" strings. No references between the definition of the literal and the usages.
So there is a third alternative called const enum
Look at this
// with a String Literal Type
type MyKeyType1 = 'foo' | 'bar' | 'baz';
// or with a String Enum
enum MyKeyType2 {
FOO = 'foo',
BAR = 'bar',
BAZ = 'baz'
}
// or with a Const String Enum
const enum MyKeyType3 {
FOO = 'foo',
BAR = 'bar',
BAZ = 'baz'
}
var a : MyKeyType1 = "bar"
var b: MyKeyType2 = MyKeyType2.BAR
var c: MyKeyType3 = MyKeyType3.BAR
will be transpiled to
// or with a String Enum
var MyKeyType2;
(function (MyKeyType2) {
MyKeyType2["FOO"] = "foo";
MyKeyType2["BAR"] = "bar";
MyKeyType2["BAZ"] = "baz";
})(MyKeyType2 || (MyKeyType2 = {}));
var a = "bar";
var b = MyKeyType2.BAR;
var c = "bar" /* BAR */;
For further playing you can check this link
I prefer the const enum case, because of the convenient way of typing Enum.Value. Typescript will do the rest for me to get the highest performance when transpiling.
The key thing to understand is that the values of string enums are opaque.
The intended use case for a string enum is that you don't want other code to know or care what the literal string backing MyKeyType.FOO is. This means that you won't be able to, say, pass the literal string "bar" to a function accepting a MyKeyType -- you'll have to write MyKeyType.BAR instead.
Typescript: string literal union type from enum - Stack Overflow
Replace enums with string literals
Typescript: Why not use string-literal types to represent enums of type string?
What's up with all the enum hate lately?
Videos
See TS4.1 ANSWER:
type WeekdayType = `${Weekday}`;
PRE TS-4.1 ANSWER:
This can't be done programmatically... you're trying to convert the type Weekday, which is Weekday.MONDAY | Weekday.TUESDAY | Weekday.WEDNESDAY, to the type WeekdayType which is "mon" | "tue" | "wed". This conversion is a form of widening, since Weekday is a subtype of WeekdayType:
type WeekdayExtendsWeekdayType =
Weekday extends WeekdayType ? true : false
// type WeekdayExtendsWeekdayType = true
Unfortunately the compiler doesn't give you a handle to remove an "enum"-ness from the enum type and leave you with plain literal types.
So, workarounds? Maybe you don't actually need an enum, but can make do with an object whose property values are string literals:
const lit = <V extends keyof any>(v: V) => v;
const Weekday = {
MONDAY: lit("mon"),
TUESDAY: lit("tue"),
WEDNESDAY: lit("wed")
}
type Weekday = (typeof Weekday)[keyof typeof Weekday],
If you inspect it, the value named Weekday behaves like an enum object:
console.log(Weekday.TUESDAY); // tue
while the type named Weekday behaves like the union of string values "mon" | "tue" | "wed" that you were calling WeekdayType:
const w: Weekday = "wed"; // okay
const x: Weekday = "xed"; // error
So in this workaround, there is no "enum"-ness, and therefore no need to distinguish the type Weekday from the type WeekdayType. It's a little different from an actual enum (which includes types like Weekday.MONDAY, which you'd have to represent as the cumbersome typeof Weekday.MONDAY or create a different type alias for it), but it might behave similarly enough to be useful. Does that work for you?
TypeScript 4.1+:
As mentioned, this can be achieved by using Template Literal Types like so:
type WeekdayType = `${Weekday}`;
TypeScript 3.4+:
Following up on @jcalz answer and the comment from @just-boris, here's an example with const assertions:
const Weekday = {
MONDAY: "mon",
TUESDAY: "tue",
WEDNESDAY: "wed",
} as const;
type Weekday = (typeof Weekday)[keyof typeof Weekday];
Conclusion: after 100+ comments and lots of discussions no one was able to come up with solid reasoning behind this trend. It looks like it's mostly personal opinions and banter. String enums are perfectly fine to use as well as any alternatives you might prefer depending on use case.
Original post:
I've noticed lately that a lot of people are taking their sweet time to hate or enums. Videos, blog posts, comments, etc. It's so weird.
I get that numeric and const enums have issues and these are valid points. But how is the conclusion that enums should be avoided at all costs?
There is also the emitting code argument, which is pretty silly and has the benefit of enums being available at runtime (as well as enumerable).
People go as far as creating const objects, which is basically creating enums with extra steps... There are even more absurd solutions out there too like const arrays casted to type unions 0_o?
And meanwhile hard coding string literals around your code base is now 'cool' and 'fine'? What's next? Hardcoding numerals? Not using env variables?
I guess for small personal/experimental projects all these alternatives might be "cool and interesting"??
I mean I've been down the rabbit hole of alternatives a couple of times myself an year or two ago only to come back to the conclusion that maintainability/simplicity > complexity/aesthetics.
Is this another one of those cancel culture things I am too old to understand? Or maybe I am missing something really important? Tell me something I don't know about the real reason to hate on enums.
Rant over.
PS: I am not saying string unions and other alternatives should not be used. They have their use cases for sure. My issue is with being particular about not using enum at all costs.