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

Support bigint literals in const enums #37783

Open
5 tasks done
connor4312 opened this issue Apr 3, 2020 · 18 comments
Open
5 tasks done

Support bigint literals in const enums #37783

connor4312 opened this issue Apr 3, 2020 · 18 comments
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript

Comments

@connor4312
Copy link
Member

connor4312 commented Apr 3, 2020

Search Terms

  • "const enum member initializers can only contain literal values"
  • "bigint enum"
  • "ts(2474)"

Suggestion

A bigint literal is a literal, but it does not seem usable in const enums.

const enum Foo {
  Bar = 123n
}

Currently produces, on 3.9.0-beta:

const enum member initializers can only contain literal values and other computed enum values. ts(2474)

Use Cases

Bigint compile-time constants 🙂

Examples

const enum Foo {
  Bar = 123n
}

console.log(Foo.Bar);

Should compile to

console.log(123n);

And log 123n when run.

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.
@RyanCavanaugh RyanCavanaugh added Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript labels Apr 3, 2020
@RyanCavanaugh
Copy link
Member

Would take a PR to improve the error message but I don't understand the use case very much. What kind of enums would be backed by bigints?

@connor4312
Copy link
Member Author

connor4312 commented Apr 3, 2020

The use case would be the similar to using normal numbers in enums--just for scenarios where we're dealing with integers that don't fit into float64's. The place I ran into this is dealing with IPv6 addresses. For IPv4 addresses, I can have an enum of well-known masks like

const enum IPv4Masks {
  MulticastMask = 0x00_ff_ff_ff,
  // ...
}

But IPv6 addresses are 64-bit integers. So, because const enums don't support bigints, I'm unable to similarly do

const enum IPv6Masks {
  MulticastMask= 0x00ff_ffff_ffff_ffff_ffff_ffff_ffff_ffffn, 
  // ...
}

You could also easily imagine large bitsets with >52 values.

const enum Permissions {
  Read = 1n << 0n,
  Write = 1n << 1n,
  // ...
  BeFancy = 1n << 58n,
}

I don't think there should be an error message at all here, since bigint literals are just another type of literal; restricting their use in const enums seems arbitrary.

@mildsunrise
Copy link

Since Numbers and BigInts don't mix, I'd vote to enforce the whole enum to be consistent: either all literals are Numbers, or all literals are BigInt.

If there are no literals, but you'd like your enum to be BigInt-backed, you'd just add = 0n to the first item:

enum Foo {
  CAT = 0n,
  DOG,  // 1n
  OWL,  // 2n
}

@connor4312
Copy link
Member Author

connor4312 commented Aug 9, 2020

Currently enums don't restrict that their values are consistent, for example this is valid:

enum Foo {
    Bar1 = 1,
    Bar2 = 'hello',
    Bar3 = true,
}

@yseymour
Copy link

You could also easily imagine large bitsets with >52 values.

Actually, >31 values, since bitwise operators in JS convert Number operands to signed 32-bit which makes the 32nd bit effectively unusable. Being able to use BigInts would be very helpful for interop with native code.

@Jack-Works
Copy link
Contributor

Yes, supporting bigint allows more flags than 31 flags

[1<<0, 1<<31, 1<<32] // [1, -2147483648, 1]
[1n<<0n, 1n<<31n, 1n<<32n] // [1n, 2147483648n, 4294967296n]

@miyaokamarina
Copy link

Possible problem with bigint enums is ambiguous reverse mapping. For example, the following code will not work as expected:

enum A {
  a = 1,
  b = 1n,
}

// Compiled to:
// A[A["a"] = 1] = "a";
// A[A["b"] = 1n] = "b";

// Expected: a
// Received: b
console.log(A[A.a]);

Also, the expression A[A.b] should be an error, because TS doesn’t allow bigints as indices.

I think, two solutions exist:

Don’t emit reverse mappings for bigint members

Just like for string members:

A[A["a"] = 1] = "a";
A["b"] = 1n;

Pros:

  • No new syntax.
  • Doesn’t require changes to other aspects of type checking.

Cons:

  • Reverse mappings don’t work.
  • Inconsistent behavior with regular number members.

Explicitly disambiguate enums with bigints using keyword

Also requires relaxing index type rules:

bigint enum A {
    a = 1, // Error.
    b = 1n,
}

bigint enum B {
    a = 1n,
}

console.log(B[B.b]); // 'a'

Compiles to:

// ...
A[A["a"] = 1] = "a";
A[A["b"] = 1n] = "b";
// ...
B[B["a"] = 1n] = "a";
// ...

console.log(B[B.b]);

Pros:

  • Reverse mappings work.
  • Consistence with other languages (C#, C++).

Cons:

  • New syntax.
  • Changes to unrelated parts of type checking.
  • New behavior to learn about: users will be unable to use regular numbers as members of these enums.
  • For consistence, may require other new enum keywords: number enum, string enum.

Another one: Only allow bigint members in const enums

Const enums are probably easier to implement as they don’t require reverse mappings. All we need is to allow BigIntLiterals in places where NumberLiterals are allowed, and to evaluate evaluate bigint expressions the same way as numeric, plus few additional checks.

Pros:

  • Easy to implement.
  • Probably, it’s enough.

Cons:

  • Inconsistent behavior with non-const enums.

Questions:

  1. How bigint constants values should be evaluated?
  2. What if build target requires bigints, but the host environment doesn’t supports them?

@miyaokamarina
Copy link

Also, reverse mapping for bigint members probably aren’t critical. These members most probably will be used for bit flags, where reverse mappings may not work even with regular numbers:

enum A {
  a = 0b01,
  b = 0b10,
}

console.log(A[A.a | A.b]); // Undefined with no error.

@miyaokamarina
Copy link

Here is a possible implementation of bigint const enums (assuming TSC host runtime supports bigints): miyaokamarina@1fbcc8e

@yseymour
Copy link

yseymour commented Oct 7, 2020

@miyaokamarina, I'd prefer just to have an error if bigint and non-bigint literals were mixed in an enum.

@miyaokamarina
Copy link

@yseymour, I don't understand why this should be an error. Both const and non-const enums already allow mixed member types. Would it be an error, if string and bigint members mixed? I think, this behavior will be inconsistent with current enums behavior.

Enums and especially const enums are underspecified after all, but I don't think this is a reason to add more strange behaviors. If enums will allow to explicitly specify underlying type, there will be no problem to warn about mixed types, but currently they don't.

@miyaokamarina
Copy link

For example, underlying types may be specified either using leading keywords (number enum, bigint const enum), or using the extends clause (enum E extends string, enum F extends bigint). But I think that's much more major change than just allow bigint literals in const enums.

@MichaelTheriot
Copy link

I just discovered this issue trying to create a constant enum for 64-bit permission bits. I would be helped by this feature.

@RubyTunaley
Copy link

Ran into this problem when writing an enum for integrated circuit signal hold times (using process.hrtime.bigint() in Node).

@SanderBouwhuis
Copy link

SanderBouwhuis commented Apr 21, 2022

I am a long time C++ developer and am now helping out with the backend which is written in typescript. I too would like to see bigint supported in enums for the purpose of replacing defined constants with bitmasks.

@abarke
Copy link

abarke commented Nov 4, 2022

Please fix this 🙏

@Andarist
Copy link
Contributor

Andarist commented Aug 1, 2023

In TS 5.0 the reported error message was changed but arguably to an equally confusing one :p

TS 5.0: const enum member initializers must be constant expressions.(2474)
TS 4.9: const enum member initializers can only contain literal values and other computed enum values.(2474)

The change wasn't really targeting this case anyhow, just noting this down here in case I decide to improve this error message anyhow.

@Sepruko
Copy link

Sepruko commented Dec 21, 2023

As previously stated by others, this feature would be useful for representing sets of bit flags larger than Number.MAX_SAFE_INTEGER, as regular numerical enums already work perfectly when you're not hitting this restriction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests