Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bitwise enum initializer: compile-time constant but not allowed as a type reference? #22709

Closed
fkleuver opened this issue Mar 20, 2018 · 4 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@fkleuver
Copy link

TypeScript Version: 2.7.2

Search Terms: const enum bitwise initializer

I'm trying to have "flags" as a type on interfaces, so that bitwise operators can be used to distinguish between various types and adjust intellisense accordingly. This works perfectly for enums and const enums where all values are literals.

The compiler won't allow it for constant values that are not literals (e.g. bitwise expressions).
It seems like an unnecessary restriction since constant expressions can easily be converted to literals anyway (even intellisense shows them as literals when you mouse over) and they can also be used in const enum.

The most relevant information I could find was this SO question's answer: https://stackoverflow.com/questions/28818849/how-do-the-different-enum-variants-work-in-typescript/

However that also does not address the distinction between non-literal constants and literals.

I can see where the check happens (checker.ts, line 20496):

if (type.flags & TypeFlags.Enum && getNodeLinks(node).resolvedSymbol.flags & SymbolFlags.EnumMember) {
    error(node, Diagnostics.Enum_type_0_has_members_with_initializers_that_are_not_literals, typeToString(type));
}

But no clarification/justification on why. See a small example below:

Code

  enum EnumA {
    One = 1
  }
  // No compiler error (as expected)
  interface InterfaceA {
    Enum: EnumA.One; // Mouseover gives: (enum member) EnumA.One = 1
  }

  enum EnumB {
    One = 1 << 0
  }
  // Compiler error: "Enum type 'EnumB' has members with initializers that are not literals."
  // why are non-literals constants not allowed?
  interface InterfaceB {
    Enum: EnumB.One; // Mouseover gives: (enum member) EnumB.One = 1
  }

  // Compiler error (as expected): In 'const' enum declarations member initializer most be constant expression
  const one = 1;
  const enum EnumC {
    One = 1 << 0 + one
  }

  // No compiler error (as expected)
  const enum EnumD {
    One = 1 << 0
  }
  

Expected behavior:
I would expect compile-time constants and literals to have the same kinds of restrictions because they evaluate to the same thing.

So either:

  • Intellisense should not show the computed value of a bitwise expression AND bitwise initializers should not be allowed in const enum, or:
  • Bitwise initializers in enums should be usable as a type reference

Actual behavior:
Intellisense shows the computed value of a bitwise expression and bitwise initializers are allowed in const enum, but they cannot be used as a type reference.

Related Issues:
#1029
#202

@RyanCavanaugh
Copy link
Member

Seems like it should be allowed. @DanielRosenwasser ?

@DanielRosenwasser DanielRosenwasser added Suggestion An idea for TypeScript In Discussion Not yet reached consensus labels Mar 20, 2018
@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Mar 20, 2018

My understanding is we don't have member types for any types of binary operations. From ze handbook

Union enums and enum member types

There is a special subset of constant enum members that aren't calculated: literal enum members.
A literal enum member is a constant enum member with no initialized value, or with values that are initialized to

  • any string literal (e.g. "foo", "bar, "baz")
  • any numeric literal (e.g. 1, 100)
  • a unary minus applied to any numeric literal (e.g. -1, -100)

When all members in an enum have literal enum values, some special semantics come to play.

The first is that enum members also become types as well!

It's not clear to me right now why we have this specific subset in mind; maybe there was a reason, but I can't recall what might stop us from just making an enum member type for every enum member whose containing enum solely holds computed values.

@fkleuver
Copy link
Author

I tried to track down the commit, it seems to be this one: f7753af

Although I don't see the actual changes in that commit, but searching for Enum_type_0_has_members_with_initializers_that_are_not_literals shows it's present here but not here and the aforementioned commit is the one between them. You tell me.. :)

@RyanCavanaugh RyanCavanaugh added Working as Intended The behavior described is the intended behavior; this is not a bug and removed In Discussion Not yet reached consensus Suggestion An idea for TypeScript labels Aug 7, 2018
@RyanCavanaugh
Copy link
Member

See comments in #26241

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

3 participants