question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Enums behave unexpectedly, the workaround is verbose. There should be a less verbose way.

See original GitHub issue

Search Terms

enums

Suggestion

Because enums do not work as expected…

const enum MyEnum {
    Zero, 
    One
} 

const foo: MyEnum.Zero = 0 // Ok as expected (since MyEnum.Zero is zero)
const bar: MyEnum.Zero = 1 // OK, but expected Error!

…people start using this workaround (aka “namespace-as-enum”):

namespace MyEnum {
  export const Zero = 0;
  export type Zero = typeof Zero;

  export const One = 1;
  export type One = typeof One;
}
type MyEnum = typeof MyEnum[keyof typeof MyEnum];
const foo: MyEnum.Zero = 0 // okay as expected
const bar: MyEnum.Zero = 1 // error as expected

Maybe there should be a less verbose way to do this common stuff? For example it could be something like enum MyEnum {type Zero, type One}

Use Cases

I use number values (i.e. enums) extensively instead of string values for performance reasons (a lot of JSON.stringify/parse).

Examples

// Syntax A...
const enum MyEnum {
    type Zero,
    type One
} 
// ...or syntax B (C++ style) 
const enum class MyEnum {
    Zero, 
    One
} 

const foo: MyEnum.Zero = 0 // Ok 
const bar: MyEnum.Zero = 1 // Error!

C++ had the same problem and they fixed it

Checklist

My suggestion meets these guidelines:

  • This wouldn’t be a breaking change in existing TypeScript/JavaScript code
  • This wouldn’t change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn’t a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • This feature would agree with the rest of TypeScript’s Design Goals.

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:35
  • Comments:16 (3 by maintainers)

github_iconTop GitHub Comments

14reactions
AnyhowStepcommented, Aug 3, 2019

Please don’t close this issue as “working as intended” or “question” or “won’t fix”. At the very least, leave it open so people can vote on it.

It’s pretty annoying that enums are basically just number just to support bit masks/flags when that’s not what an enum is really supposed to be

https://github.com/microsoft/TypeScript/issues/31834

https://github.com/microsoft/TypeScript/issues/30629

https://github.com/microsoft/TypeScript/issues/26362

https://github.com/microsoft/TypeScript/issues/22464

https://github.com/microsoft/TypeScript/issues/22311

There are probably a bunch of other issues related to this.

The point being that enums, as they are now, are only really useful if you use bit masks and don’t care about variables of that type being any number.

However, that runs counter to how one usually thinks about enums. If anything, people that use bitwise operators on numeric enum values should get back a number that is not assignable to the original enum type.

If backwards compatibility for this thing is so important, then, as above, new syntax for “proper” enums should be introduced that’s a shorthand for the namespace-as-enum workaround.

I don’t have hard numbers but I feel like most people who use numeric enums expect it to behave like the namespace-as-enum workaround.


Whenever someone asks me why their enum code isn’t working (at work, on gitter, where ever), I just tell them because enums are broken and to not use them. It would be nice to have it unbroken


Maybe introduce syntax like enum class (like in c++). Except, cpp enum classes are a little too restrictive.

It would be nice if TS enum classes would have the behavior of the namespace-as-enum workaround.

//Maybe call it `enum namespace`? `enum interface`?
enum class MyEnum {
  Zero,
  One,
}
//OK
const x : MyEnum.One = 1;
//Error
const y : MyEnum.One = 0;

//OK
const a : 1 = MyEnum.One;
//Error
const b : 1 = MyEnum.Zero;

//Error
const e : MyEnum = 2;
11reactions
jhnnscommented, Dec 21, 2019

Just stumbled upon this and I have to say that I’m really surprised about the current behavior. To me, the keyword enum clearly communicates that only the enumerated values are allowed, not just all numbers. And what’s even more surprising is that enums do work as expected with strings. They’re even super-strict with strings, treating enum members effectively as symbols:

enum State {
    Success = "SUCCESS",
    Error = "ERROR",
}

const state: State = "SUCCESS"; // Error, because it's not State.Success
                                // This is how symbols work

To me, this behavior is very unexpected and also inconsistent. And given the long list of issues (#17734, #21546, #11559, #15591, #8020, #18409, …) I’m not the only one that finds that surprising.

I do understand that this is a massive breaking change, especially for the TypeScript project itself. But I think that this needs to be addressed somehow. I honestly never used bit masks in my projects, but I do use enums all the time and there are a lot of places where I relied on the exhaustiveness check (which clearly doesn’t work as you can pass in any number).

What about a symbol enum or enum symbol keyword:

enum symbol State {
    Success,
    Error,
}

// Syntactic sugar for:

const State = {
    Success: Symbol("Success") as unique symbol, // as unique symbol is currently not
    Error: Symbol("Error") as unique symbol,     // allowed but you probably get the gist :)
};

type State = typeof State.Success | typeof State.Error;

let state: State;
state = State.Success; // Works
state = "Success"; // Error, because it must be the State.Success symbol

This would also be a good trade-off between easy to write enums that do not require you to re-spell the string literal (as string enums currently do) while maintaining debuggability because of symbol descriptions.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Is there a less verbose way to retrieve non deprecated enum ...
Here is a more concise solution using streams: public enum MyEnum { AA, BB, @Deprecated CC, DD, @Deprecated EE, /** * Retrieve enum...
Read more >
C++11: Non-intrusive enum class with Reflection support ...
The Reflection issue describes the ability to do things like "Enumerate all values", "Enumerate member strings", "Convert to/from string", "Safe ...
Read more >
Google TypeScript Style Guide
This Style Guide uses RFC 2119 terminology when using the phrases must, must not, should, should not, and may. All examples given are...
Read more >
Unreasonable warning for enum-based switch statements
(In reply to comment #19) > ... For some enums, a human can judge that there's likely a > good action for new...
Read more >
To panic! or Not to panic! - The Rust Programming Language
The calling code could choose to attempt to recover in a way that's ... having lots of error checks in all of your...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found