Skip to content

const enum: numeric name #3572

Closed
Closed
@jbondc

Description

@jbondc
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.

Activity

kitsonk

kitsonk commented on Jun 19, 2015

@kitsonk
Contributor

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

jbondc

jbondc commented on Jun 19, 2015

@jbondc
ContributorAuthor

typeof "NaN" === "string" ?

jbondc

jbondc commented on Jun 19, 2015

@jbondc
ContributorAuthor

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

kitsonk commented on Jun 19, 2015

@kitsonk
Contributor

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

DanielRosenwasser commented on Jun 19, 2015

@DanielRosenwasser
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.

added
By DesignDeprecated - use "Working as Intended" or "Design Limitation" instead
SuggestionAn idea for TypeScript
on Jun 19, 2015
jbondc

jbondc commented on Jun 19, 2015

@jbondc
ContributorAuthor

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

jbondc

jbondc commented on Jun 19, 2015

@jbondc
ContributorAuthor

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

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

jbondc commented on Jun 19, 2015

@jbondc
ContributorAuthor

I was writing this code:

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

DanielRosenwasser commented on Jun 19, 2015

@DanielRosenwasser
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

RyanCavanaugh commented on Jun 19, 2015

@RyanCavanaugh
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

DanielRosenwasser commented on Jun 19, 2015

@DanielRosenwasser
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

jbondc commented on Aug 21, 2015

@jbondc
ContributorAuthor

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.

locked and limited conversation to collaborators on Jun 19, 2018
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

    By DesignDeprecated - use "Working as Intended" or "Design Limitation" insteadSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @jbondc@DanielRosenwasser@kitsonk@RyanCavanaugh

        Issue actions

          const enum: numeric name · Issue #3572 · microsoft/TypeScript