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 Overflow
Top answer
1 of 5
79

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.

2 of 5
78

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.

🌐
Reddit
reddit.com › r/typescript › use string literal instead of enums!
r/typescript on Reddit: Use string literal instead of enums!
July 15, 2024 - We’re using TypeScript just for type checking, so let’s do it in a way that doesn’t add any extra ... Enums don't add runtime overhead if used as types with the type checker. They only add runtime overhead when used at runtime. Which is something you can't do with string literals, so this is a win for enums.
Discussions

Typescript: string literal union type from enum - Stack Overflow
I'd like to get a string literal union from an enum. For this enum… enum Weekday { MONDAY = 'mon', TUESDAY = 'tue', WEDNESDAY = 'wed' } … I'd like to get this: type WeekdayType = 'mo... More on stackoverflow.com
🌐 stackoverflow.com
Replace enums with string literals
Let's replace enums with string literals. Instead of something like export enums RegionCardinality { CELLS, FULL_COLUMNS, } we can have something like export type RegionCardinality = "cell... More on github.com
🌐 github.com
15
August 1, 2016
Typescript: Why not use string-literal types to represent enums of type string?
Description The typescript-angular2 generator currently emits enums in the form export enum StatusEnum { Open = 'open', InProgress = 'inProgress', Resolved = 'resolved', Closed = ... More on github.com
🌐 github.com
2
June 15, 2017
What's up with all the enum hate lately?
IMO, the 'const object' is the best case for most folks till TC39 decide what to do about enums. Which is why I added that method to the handbook page on enums - https://www.typescriptlang.org/docs/handbook/enums.html#objects-vs-enums Aligning with the future of JavaScript is the main reason, small nicities in syntax/errors are nice but eventually you'll have to migrate that codebase to 'real' enums anyway and who knows what that migration path looks like. It's like staking your codebase on the experimental decorators option, maybe convenient but you're making work for yourself later down the line. More on reddit.com
🌐 r/typescript
122
69
January 6, 2023
🌐
TypeScript
typescriptlang.org › docs › handbook › enums.html
TypeScript: Handbook - Enums
In a string enum, each member has to be constant-initialized with a string literal, or with another string enum member.
🌐
Ben Holmes
bholmes.dev › blog › using-enums-with-string-values-in-typescript-consider-string-literals-instead
Using Typescript string enums? Consider string literals!
July 29, 2019 - In fact, it'll often provide more information than necessary when assigning the literal type. It's also more visually unclear what all possible values are when assigning 'random string' instead of SpecificTypes.Enum.
🌐
Brockherion
brockherion.dev › blog › posts › enums-vs-typed-strings-in-typescript
Enums vs String Literal Types in TypeScript
October 11, 2022 - String literal types have the advantage of not generating any extra code when we build our application, which leads to a smaller JavaScript bundle. This type is only used by TypeScript at compile time.
🌐
TypeScript Course
typescriptcourse.com › string-literal-unions-over-enums
Why You Should Use String Literal Unions Over Enums in TypeScript
Enums break that rule. They can be compiled to JS, thus increasing the bundle size. However with a union of string literals, no code is emitted aka no increase in bundle size. This means you can have a long list of string literals in a union without an increase in bundle size. ... Learning TypeScript is tough.
🌐
Daniel Barta
danielbarta.com › typescript-literal-types-are-stronger-than-enums
TypeScript's Literal Types Are Better Than Enums - Daniel Barta
May 9, 2022 - Enums in TypeScript are as powerful as in any other language, but with TypeScript's support for unioned literal types, it is possible to have all the compile-time benefits of the enums without the need to import an enum every time.
Find elsewhere
🌐
HTML Goodies
htmlgoodies.com › home › javascript
Choosing Between TypeScript String Literals and Enums | HTMLGoodies
August 11, 2021 - type TimeFrames = 'hour' | 'day' | 'week' | 'month' | 'year'; enum TimeFrames { HOUR = 'hour', DAY = 'day', WEEK = 'week', MONTH = 'month', YEAR = 'year' } ... One of the main differences can only be observed by looking at the transpiled code. In the case of of unions of string literals, TypeScript doesn’t need to generate code, because it’s only used at compile time.
🌐
DEV Community
dev.to › bholmesdev › using-enums-with-string-values-in-typescript-consider-string-literals-instead-486e
Using Typescript string enums? Consider string literals! - DEV Community
April 11, 2021 - In fact, it'll often provide more information than necessary when assigning the literal type. It's also more visually unclear what all possible values are when assigning 'random string' instead of SpecificTypes.Enum.
🌐
Campedersen
campedersen.com › enum-vs-string
Enums vs String Literals in TypeScript - Cam Pedersen
The less code you ship the better, and as stated above enums will generate a value via compiled JS. If you don't need to attach extra info (like a name, or display value) to the data, a string literal will do just fine to define your object and give you that sweet TS autocompletion...
🌐
Thisthat
thisthat.dev › literal-union-type-vs-string-enums
literal union type vs string enums - this vs that
enum Theme { DEFAULT = 0, LIGHT = 1, DARK = 2, } // TypeScript doesn't throw errors const theme: Theme.DEFAULT = 3; Due to these reasons, it's advised to use string literals for the enum values.
🌐
LogRocket
blog.logrocket.com › home › typescript string enums, and when and how to use them
TypeScript string enums, and when and how to use them - LogRocket Blog
August 13, 2024 - As we mentioned earlier, while enums are numerically based by default, TypeScript ≥ version 2.4 supports string-based enums. String-based enums, just like object literals, support computed names with the use of the square bracket notation, ...
🌐
Stephencharlesweiss
stephencharlesweiss.com › typescript-enums-vs-string-literals
typescript: enums vs. string literals | /*code-comments*/
As of yet, I haven’t found a case where enum worked better, more clearly or more safely than a string literal type. One advantage of string literals is that you can leverage generics with pick/keyof.
Top answer
1 of 5
243

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?

2 of 5
75

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];
🌐
Nikolasbarwicki
nikolasbarwicki.com › home
Enums vs string literal types in Typescript - Nikolas's Blog
July 20, 2024 - Your SUPER-powered WP Engine Blog · Check out the latest posts
🌐
GitHub
github.com › palantir › blueprint › issues › 324
Replace enums with string literals · Issue #324 · palantir/blueprint
August 1, 2016 - Let's replace enums with string literals. Instead of something like export enums RegionCardinality { CELLS, FULL_COLUMNS, } we can have something like export type RegionCardinality = "cells" | "full-columns" export const RegionCardinalit...
Author   michael-yx-wu
🌐
GitHub
github.com › swagger-api › swagger-codegen › issues › 6206
Typescript: Why not use string-literal types to represent enums of type string? · Issue #6206 · swagger-api/swagger-codegen
June 15, 2017 - Generate enums of type string as union of string literals, leaving enums of numeric type as they are (they need special treatment anyway to fix #3500).
Author   bedag-moo
🌐
2ality
2ality.com › 2020 › 02 › enum-alternatives-typescript.html
Alternatives to enums in TypeScript
February 22, 2020 - With type unions, you get auto-completion in place or – if it’s a union of string literal types – inside string literal quotes. Exhaustiveness checks work for both. But they also differ. Downsides of unions of symbol singleton types are: They are slightly verbose. There is no namespace for their members. It’s slightly harder to migrate from them to different constructs (should it be necessary): It’s easier to find where enum member values are mentioned. ... They are not a custom TypeScript language construct and therefore closer to plain JavaScript.
🌐
Reddit
reddit.com › r/typescript › what's up with all the enum hate lately?
r/typescript on Reddit: What's up with all the enum hate lately?
January 6, 2023 -

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.

🌐
LogRocket
blog.logrocket.com › home › typescript enums vs. types: enhancing code readability
TypeScript enums vs. types: Enhancing code readability - LogRocket Blog
November 26, 2024 - Using a union of string literals, TypeScript performs static type checking and ensures that only the specified string literals are allowed for variables of the declared type.