Repository: TypeScript
Issue: Enum with special numbers overwrites own value
Labels: Bug,Breaking Change

# 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](https://www.typescriptlang.org/play?ts=4.6.2#code/PQKhCgAIUgVALAlgZ0gUwHYFcC2kDuA9gE4DWqAhqmgB4AOaAxgC5oAmUIw4mukAovSas2kAN5RIUgGIB5WZAC8kAOQAzQoRUAaSVIBCAQQBKSyAEZdUqSsgBFAKqzY-ACKqzKx87crwAX3BwRkIMZEIAGzQAOgjCAHMACkEGFnZouQVFbNUNLQBKAG5IYGBIZmIsNGDQ8KjYhOShNLZoo1Ns5XMikrKKqpqwyJi4pJThdgBtW28XdxUAXSUcryc5lR7S8srqwbqRxvGWjPll5RUAZUIcNEhCZng0YkhkCsQMeI3irbUKCOQ0NpIBQMKJmABPBjIRjERB0ZgECjEMJ7YYNMbNERtExnSAAJk2ZV+-0BwNB5UhaGhsPhiORyCCoAg0DgSGo2DwbEIVMgGHuBBIpGB1Ex7CsMBBogoLCwfwi4LuADcnvhYaxUIhmKhCPgMJBFX8qshopxuCEwgjCAAjABWwgAMpqnn8zGJ-IUeBzIABha50LAicSSACSGDU701CuUofDGEjVkgADkKImzMnEwmVABaPHRPF4gAMABYiwA2YsATjxAFYCwBmcx1tBZ8yl2zKXP54tlys1+uN5uthNbWAXSAAdSRYUgGmeDxQ+qeyEQoUgiStAd5-NnwIiEUXxGXtWA5vD8WQ+SBG4Rr0Qe8gIRwdDvVMkW19OBuGEtm+YhEgxBoDghDKnScYfJIKiTNadosJAsi2sICztnciEsI6rDEH8CbYPeyi4REASovUoyJB+-pYjGEYQriVFxhC+S9DOfwAsRBxJORAbpHRka4ioPEQhsTH9LciRoMqeoPIQWDxPAkCag+foRBQ7wXkE5r7OiZF+lxrTpri6aMT8LHVBpaKkZxWL6Z0qjpkJWwiWu4mYOU8DSbJ8kIo+dDKap+TqbU5mNJZUxQTBwjwWhzDIUsNnhehTpYRERlEiZbFaSFbDTNBUWRbB0WLFINlhblCH5chKXbFUTkSa57lyQp3m+WE-npRZOlYgRuIEZVxKsWZJHBR16RdcVBH2X0Ow1S5UkyQ1XlKSpLVAA)

### 💻 Code

```ts
enum Computed {
  Infinity = Infinity,
  NaN = NaN
}
```

### 🙁 Actual behavior

Typescript generates the following code for that enum using a "lookup object":
```ts
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
```ts
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.

```ts
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
```ts
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
```ts
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"
```ts
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.
    ```ts
    enum Allowed {
        FOO = 1 + 2
    }
    enum NotAllowed {
        BAR = 1 / 0
    }
    ```