Skip to content

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

Description

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

Activity

  1. RyanCavanaugh commented on Mar 20, 2018

    @RyanCavanaugh
    Member

    Seems like it should be allowed. Daniel Rosenwasser (@DanielRosenwasser) ?

  2. DanielRosenwasser commented on Mar 20, 2018

    @DanielRosenwasser
    Member

    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.

  3. fkleuver commented on Mar 20, 2018

    @fkleuver
    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.. :)

  4. added
    Working as IntendedThe behavior described is the intended behavior; this is not a bug
    and removed
    SuggestionAn idea for TypeScript
    on Aug 7, 2018
  5. RyanCavanaugh commented on Aug 7, 2018

    @RyanCavanaugh
    Member

    See comments in #26241

  6. locked as resolved and limited conversation to collaborators on Oct 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Working as IntendedThe behavior described is the intended behavior; this is not a bug

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions