Skip to content

const enum: numeric name #3572

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

Closed
jbondc opened this issue Jun 19, 2015 · 12 comments
Closed

const enum: numeric name #3572

jbondc opened this issue Jun 19, 2015 · 12 comments
Labels
By Design Deprecated - use "Working as Intended" or "Design Limitation" instead Suggestion An idea for TypeScript

Comments

@jbondc
Copy link
Contributor

jbondc commented Jun 19, 2015

const enum foo {
    NaN = 1, // error: An enum member can not have a numeric name
    Infinity = 2 // error: An enum member can not have a numeric name
}

Don't think those should be errors.

@kitsonk
Copy link
Contributor

kitsonk commented Jun 19, 2015

Well, enums can't have numeric names and NaN and Infinity are actually numeric symbols. typeof NaN === 'number' and typeof Infinity === 'number'.

@jbondc
Copy link
Contributor Author

jbondc commented Jun 19, 2015

typeof "NaN" === "string" ?

@jbondc
Copy link
Contributor Author

jbondc commented Jun 19, 2015

An enum is indexed with strings not numeric things:

const enum foo {
    [x: string]: number
    NaN = 1, // error: An enum member can not have a numeric name
    Infinity = 2 // error: An enum member can not have a numeric name
}

@kitsonk
Copy link
Contributor

kitsonk commented Jun 19, 2015

Hmmm interesting:

const enum foo {
    const, // valid
    let, // valid
    var, // valid
    typeof, // valid
    'Hello', // valid
    '1', // error: An enum member can not have a numeric name
    'NaN', // error: An enum member can not have a numeric name
    'Infinity' // error: An enum member can not have a numeric name
}

@DanielRosenwasser
Copy link
Member

This has nothing to do with typeof. This has to do with the fact that when indexing with a value equal to NaN (e.g. 0/0), you are actually indexing with "NaN". If we don't check to make sure an enum name is numeric, then the reverse-mapping no longer applies - in the following example, even though the expression indexed with is of type number, you'll get 10 (which is also a number) instead of "A".

enum E {
    A = NaN,
    NaN = 10,
}

E[0/0];

Basically what we look for is whether the abstract operation ToNumber and then ToString matches the text of the enum name. See the explicit logic. See the comment that precedes it.

Whether or not it matters for const enums is arguable. Considering you can still have a reverse mapping with --preserveConstEnums, and that it's undesirable to have too many semantic differences between enums and const enums, I'm inclined to say that this is by design unless there's a sufficiently useful use case.

@DanielRosenwasser DanielRosenwasser added By Design Deprecated - use "Working as Intended" or "Design Limitation" instead Suggestion An idea for TypeScript labels Jun 19, 2015
@jbondc
Copy link
Contributor Author

jbondc commented Jun 19, 2015

@DanielRosenwasser But NaN can't be assigned to a const enum

@jbondc
Copy link
Contributor Author

jbondc commented Jun 19, 2015

It also isn't a valid value for ambient/declared enums:

declare enum foo {
  NaN = 1;
  Foo = NaN; // error
}

@jbondc
Copy link
Contributor Author

jbondc commented Jun 19, 2015

I was writing this code:

    const enum BitValues {
        Undefined = 1,
        Null = 2,
        True = 4,
        False = 8,
        NaN = 16, // NaN cannot have numeric name?
    }

@DanielRosenwasser
Copy link
Member

That is a decent use case, but I'd still rather not have const enum rules deviate too much from enum rules. Perhaps others can weigh in.

@vladima and @mhegazy might have more insight as to why NaN and +/-Infinity can are not valid RHSs in enum members.

It also isn't a valid value for ambient/declared enums:

I don't believe it should be; declaring an enum implies that there exists a variable with the same mapping semantics at runtime.

@RyanCavanaugh
Copy link
Member

We disallow NaN and Infinity because we consider them to be numeric names (because (+'NaN').toString() === 'NaN'); i.e. it's the same reason you can't write enum X { '0': 1 }.

We could reasonably remove that restriction for identifier-like things because it's very unlikely you would actually have an enum member whose value was one of those non-real numbers.

@DanielRosenwasser
Copy link
Member

We could reasonably remove that restriction for identifier-like things because it's very unlikely you would actually have an enum member whose value was one of those non-real numbers.

If you mean for all enums, I don't see why, since we've already settled on a reasonably consistent semantics.

If you mean for const enums, I feel like that would introduce more inconsistencies between the two which is undesirable.

@jbondc
Copy link
Contributor Author

jbondc commented Aug 21, 2015

It could be as simple as disabling NaN or Infinity in the reverse-mapping, don't think there's any use case there.

enum a {
   NaN = 0,
   Infinity = 1,
   a = NaN,
   b = Infinity,
}

Emits

var a;
(function (a) {
    a[a["NaN"] = 0] = "NaN";
    a[a["Infinity"] = 1] = "Infinity";
    a["a"] = NaN;
    a["b"] = Infinity;
})(a || (a = {}));

What I find more inconsistent is this:
snap

I've talked before about changing the semantics for the reverse mapping which I'd love to see.

@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
By Design Deprecated - use "Working as Intended" or "Design Limitation" instead Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

4 participants