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

Unable to use enum with const modifier because of inline optimization #21391

Closed
stieg opened this issue Jan 24, 2018 · 8 comments
Closed

Unable to use enum with const modifier because of inline optimization #21391

stieg opened this issue Jan 24, 2018 · 8 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@stieg
Copy link

stieg commented Jan 24, 2018

It seems there is a design shortcoming with const enum in TS because there is no way to get a list of all values without changing const enum to a plain old enum. This is a problem because there are cases where a developer would want to use a const enum but can not because they can not programatically get a list of all possible values (due to the inlining optimization).

In example suppose a developer wants to use a const enum to represent a field in a mongoose schema. Here is an example of what this code might look like:

const enum Sex {
  FEMALE = "Female",
  MALE = "Male",
}

interface IUser  {
  firstName: string,
  lastName: string,
  sex: Sex,
}

If this developer were to try and create a Mongoose schema, they would not be able to enforce strong types on this sex field they would fail because they would not have access to all the possible const enum values.

export const UserSchema: Schema = new Schema({
  firstName: String,
  lastName: String,
  sex: {
    enum: Object.values(Sex),   // Won't Work because Sex is optimized out.
    type: String,
  },
}, {});

The only way for me to get this to work is to remove the const declaration from the Sex enum. That is not what I want to do because I have no intention of adding additional Sex values to that enum. Thus this enum should be able to be declared as a const enum but can not be because of the aforementioned reasons. This seems like a bug or an oversight. What should be possible here is for me to call some special reserved property on the enum that will return a list of all keys for that enum. In example I would expect something like the following to work in the ideal scenario:

export const UserSchema: Schema = new Schema({
  firstName: String,
  lastName: String,
  sex: {
    enum: Sex._values,
    type: String,
  },
}, {});

This would allow me to use const enum types and also would allow me to know all possible values of the enum itself. Another way might be to add a special keyword like valuesof to get them. So in code it would look like valuesof Sex which would return the array ['Female', 'Male']. Thoughts?

@mhegazy
Copy link
Contributor

mhegazy commented Jan 24, 2018

Thus this enum should be able to be declared as a const enum but can not be because of the aforementioned reasons. This seems like a bug or an oversight

const enums are erased at compile time. they do not exist at runtime. your request here to have a helper that gets you the keys at runtime goes against the design of the const enum features.

I would recommend using regular enums instead.

@mhegazy mhegazy added the Working as Intended The behavior described is the intended behavior; this is not a bug label Jan 24, 2018
@stieg
Copy link
Author

stieg commented Jan 24, 2018

@mhegazy I understand this about const enum types and how TS inlines them. However I should be able to use a const enum and have some way to access the values of that enum without having to drop the const modifier. How the TS transpiler allows me to access them I care not about; just so long as there is a way.

I feel like I'm taking crazy pills here since I am getting the impression that the TS devs promote the const modifier as an optimization over its intended purpose of denoting that a value will never change. Is this not what you are doing?

Of course I can drop the const modifier and get this around this problem; but that is a workaround to this problem and workarounds to problems should never be considered a proper solution.

@mhegazy
Copy link
Contributor

mhegazy commented Jan 24, 2018

@mhegazy I understand this about const enum types and how TS inlines them. However I should be able to use a const enum and have some way to access the values of that enum without having to drop the const modifier. How the TS transpiler allows me to access them I care not about; just so long as there is a way.

That is kinda what i said before, but this is what const enum means. seems like you want to use regular enums, with an optional --inlineConstants..

@mhegazy
Copy link
Contributor

mhegazy commented Jan 24, 2018

I feel like I'm taking crazy pills here since I am getting the impression that the TS devs promote the const modifier as an optimization over its intended purpose of denoting that a value will never change. Is this not what you are doing?

well.. i am telling you, it's "intended purpose" is to be removed and inlined, and not to be "constant". all enum values are constant by definition. const enums do merge as well, just like regular enums.

@RyanCavanaugh
Copy link
Member

const means two things:

  • Don't emit anything for the enum construct itself
  • Always inline the enum values

Non-const means two things:

  • Emit the key/value lookup object
  • Don't inline the enum values

Enum values are never mutable; this code is illegal regardless of const:

enum X { Y }
X.Y = 3;

If you want the enum object to be present at runtime for debugging purposes, use --preserveConstEnums.

There are many different aspects to enum and we don't have flags/modifiers to fill in every possible cell in the matrix of possible behaviors.

@typescript-bot
Copy link
Collaborator

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

@strogonoff
Copy link

IMO this is not about flags, this is about broken semantics of const.

In TypeScript, const is on a completely different level of abstraction when used with enum as compared to its established meaning in all other cases. As a practical consequence, one can enumerate an enum declared without const but not with const. More generally, it taints const by making low-level implementation knowledge mandatory in order to use it properly.

+1 on reopening this, my current opinion is it’s a language design issue and the right solution is to ensure syntax used for compiler optimizations can’t be confused with syntax of high-level language features.

Might be unlikely to change as it’s an entrenched feature by now.

@RyanCavanaugh
Copy link
Member

RyanCavanaugh commented Feb 13, 2018

Our options are extremely limited because we're trying to not break existing code. Today the program

foo
enum X { Y }

means "the expression statement foo; and an enum declaration".

If we were to try to co-opt the foo spelling to mean something else, then we would change the semantics of that text.

Prior to const enum existing, the program

const
enum X { Y }

was a syntax error. The only things we can write in place of foo that have this property are the existing keywords and reserved words, so we necessarily have to co-opt an extant keyword if we want to modify an existing declaration form.

@microsoft microsoft locked and limited conversation to collaborators Jul 3, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

5 participants