Skip to content

Enum with special numbers overwrites own value #48956

Open
@JacobLey

Description

@JacobLey

Bug Report

🔎 Search Terms

Enum number computed overwrite Infinity NaN

🕗 Version & Regression Information

I noticed on 4.6.2, but using TS Playground am able to reproduce on earliest possible version (3.3.3). Also still occurs on nightly build.

⏯ Playground Link

Playground link with relevant code

💻 Code

enum Computed {
  Infinity = Infinity,
  NaN = NaN
}

🙁 Actual behavior

Typescript generates the following code for that enum using a "lookup object":

var Computed;
(function (Computed) {
    Computed[Computed["Infinity"] = Infinity] = "Infinity";
    Computed[Computed["NaN"] = NaN] = "NaN";
})(Computed || (Computed = {}));

As a result, the Computed enum actually has values of Strings, rather than the "computed" numbers as expected

console.log(Computed);
// { Infinity: 'Infinity', NaN: 'NaN' }

Note the "lookup object" behavior itself is not unexpected, but the side effect of overwriting keys is unexpected.

🙂 Expected behavior

Typescript should not overwrite an existing key with a new value. Most likely that would mean disabling the "lookup values" for computed.

console.log(Computed);
// { Infinity: Infinity, NaN: NaN }

Extra Discussion

This does not impact const enum because values like Infinity are considered "computed".

It is also possible to do something like

enum Computed {
  '[object Object]' = {},
  null = null
}

with similar results.
The latest versions of typescript warn at this behavior, but it is possible to compile and I don't think it warns on all versions. But I omitted from core issue for that reason.

Similarly

enum Overwrite {
  123 = 'FOO',
  BAR = 123,
}

has similar behavior (when compiled) but TS does warn about numeric names (I believe this warning occurs in all versions).

It does not occur when using string enums, as those never write "lookup objects"

enum NoOverwrite {
  FOO = 'SOME VAL',
  BAR = 'FOO',
  BAZ = 123
}
console.log(NoOverwrite);
// { 123: "BAZ", FOO: "SOME VAL", BAR: "FOO", BAZ: 123 }

Effectively this issue happens whenever the String(val) (generally .toString() internally) returns a key in the Enum.

Possible solutions:

  • Don't emit "lookup object"
    • I'm going to assume the lookup object behavior is intentional/beneficial for literal numbers, but perhaps not for computed... Although values like 1 + 2 are "computed" but are resolved at compile time. So maybe "computed" where the value is anything but a number literal.
  • Forbid special "numeric" names
    • Don't allow NaN, Infinity or -Infinity as an enum key name. There may be others I am not aware of right now... but I was not able to reproduce error with exponential notation like Number.EPSILON ("An enum member cannot have a numeric name.")
  • Don't allow computed values that are non numeric literals
    • Throw compile-time error when value is something like Infinity, but allow literals.
      enum Allowed {
          FOO = 1 + 2
      }
      enum NotAllowed {
          BAR = 1 / 0
      }

Metadata

Metadata

Assignees

No one assigned

    Labels

    Breaking ChangeWould introduce errors in existing codeBugA bug in TypeScript

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions