Angular-cli: const enum is broken

Created on 17 Oct 2016  路  21Comments  路  Source: angular/angular-cli

OS?

Mac OSX Sierra

Versions.

1.0.0-beta.17

Repro steps.

ng new --prefix=t check-enum
cd check-enum/
ng serve

then add to main.ts (no matter, may be any *.ts)

declare namespace Sts {
  export const enum A{
    a, b, c
  }
}
console.log(Sts.A.a, Sts.A.b);

The log given by the failure.

main.bundle.js:65131 Uncaught ReferenceError: Sts is not defined

Actual in main.bundle.js

console.log(Sts.A.a, Sts.A.b); (see at Source panel of Chrome Developer Tools)

Expected in main.bundle.js

console.log(0 /* a */, 1 /* b */);

If I run tsc from src directory, this code will be in dist/out-tsc, as expected. TypeScript is OK!

Mention any other details that might be useful.

I donn't know, where is a mistake, in angular-cli or its configuration or deps. Generated code is same, as without const keyword. This is very strange.

investigation

Most helpful comment

@sumitarora The problem isn't that we can't compare const enums, it's that the values we're comparing against aren't transpiled (and thus result in runtime errors). If we take your example (a little shortened):

// Ambient declaration in another .d.ts file
namespace Validation {
  export const enum A {
    a, b, c
  }
}

// Usage code
const x: Validation.A = Validation.A.c;
switch (x.valueOf()) {
  case Validation.A.a:
    console.log('a');
    break;
}

Should transpile to:

var x = 2;
switch (x.valueOf()) {
    case 0:
        console.log('a');
        break;
}

But instead transpiles to:

var x = Validation.A.c;
switch (x.valueOf()) {
    case Validation.A.a:
        console.log('a');
        break;
}

And naturally it cannot find Validation.A.c and Validation.A.a at runtime.

All 21 comments

This is blocking me as well. version 1.0.0-beta.25.5

We also ran into this while upgrading to a Webpack-based/AOT-enabled build system. We have some tooling set up that automatically generates d.ts files for all of our web-facing model classes. This maps native enums - very correctly - to const enums.

The automatically generated .d.ts file:

declare module server {
    const enum Status {
        OK
    }
}

The problem arose when we tried to use these enum values directly:

export function processStatus(status: server.Status) {
    switch (status) {
        server.Status.OK:
            // Custom code here
            break;
    }
}

Luckily code like this was few and far-between so I solved the issue by replacing the generated enums with their constant values. This solution is of course very far from ideal: we lose type safety and the code becomes harder to read. Non-ambient const enums are properly transpiled.

Since this issue is 6 months old and is a pretty severe codegen bug some official word would be really appreciated.

It still be very annoying bug.
Did anybody try to fix or "investigate" this?

I found explanation: Const enums are preserved when using transpileModule

In short: transpileModule compiles in single-file mode, so it don't know about enums in other files.

Now I don't use const enums(

preserveConstEnums = true not works in ambient declaration files (*.d.ts)

Third way is too complex and takes much time. It is very expensive in support.

Thanks!

Dear CLI team! Please give some feedback on the issue.
I have similar setting as Gustorn (post from 10 Apr.) and feel very uncomfortable modifing generated code just to satisfy the CLI tooling pipeline.
"awesome-typescript-loader" still does not have any solution for the problem, so may be you can replace the loader?
Thanks!

@taras-demyanets @KhodeN You can compare enums using valueOf function of enums.

namespace Validation {
  export const enum A {
    a, b, c
  }
}

const x: Validation.A = Validation.A.c;
console.log(x.valueOf() === Validation.A.b); // return false

switch (x.valueOf()) {
  case Validation.A.a:
    console.log('a');
  break;
  case Validation.A.b:
    console.log('b');
  break;
  case Validation.A.c:
    console.log('c');
  break;
  default:
    console.log('none');
  break;
}

@sumitarora The problem isn't that we can't compare const enums, it's that the values we're comparing against aren't transpiled (and thus result in runtime errors). If we take your example (a little shortened):

// Ambient declaration in another .d.ts file
namespace Validation {
  export const enum A {
    a, b, c
  }
}

// Usage code
const x: Validation.A = Validation.A.c;
switch (x.valueOf()) {
  case Validation.A.a:
    console.log('a');
    break;
}

Should transpile to:

var x = 2;
switch (x.valueOf()) {
    case 0:
        console.log('a');
        break;
}

But instead transpiles to:

var x = Validation.A.c;
switch (x.valueOf()) {
    case Validation.A.a:
        console.log('a');
        break;
}

And naturally it cannot find Validation.A.c and Validation.A.a at runtime.

I've many enums in my project and that I can't use them is a really show stopper for me to use the cli.

Currently I am using pure webpack 2 and there everything is workingfine with the Enums here my TS loader is the following rule

test: /\.ts$/,
       use: [
         '@angularclass/hmr-loader?pretty=' + !isProd + '&prod=' + isProd, 
         'awesome-typescript-loader',
         'angular2-template-loader'
     ],

I have a .NET MVC Project and I am using TypeLite to generate TypeScript Enums out of my C# Enums. Here a Enums.ts file is created with many Enums inside like

module  Internal.Notification.DomainModel {
  export const enum NotificationType {
    Information = 0,
    Success = 1,
    Warning = 2,
    Error = 3
   }
}
module  Internal.Repository.DomainModel {
  export const enum SortDirectionEnum {
    Ascending = 0,
    Descending = 1
   }
}

I am currently trying to migrate from my own webpack 2 build to the cli but currently it seems not possible.

@imiuka your Solution with "preserveConstEnums" is not working here and the third solution is too complex, there I can stay with my current own WebPack Build Setup.

@Gustorn why is the third Solution from @imiuka not implemented in the CLI with an addional Parameter or so and perhaps can someone explain me why my TypeScript Loader setup makes no Problems with the Enums?

A working solution for such a common problem would be really nice.

This is still a blocking issue for me.
Repro steps:

  • create a new project (ng new)
  • add a new ts file (enums.ts for example)
  • make the contents of this file
    module enums { export const enum myEnum { value0 = 0 } }
  • add this file to the tsconfig.app (using "files" or "include" for example)
  • reference your enum in app.component.ts
    let x = enums.myEnum.value0;
  • ng serve (or test or e2e)

Expected:
The app builds and runs without error
Actual:
The app builds without error. However, the module containing your enum will not be defined at runtime. Error message: Uncaught ReferenceError: enums is not defined.

The module not being defined at runtime is actually expected. The compiler _should have_ replaced the enum reference with the value because this enum is defined as a constant. This is unexpected and undesirable behavior.

This issue prevents me from using angular-cli in projects where I have split out enumerations into their own files for reuse. What is worse is that I can not use tools like typelite to automatically translate my enumerations from .Net to typescript.

@angular/cli: 1.0.4
node: 6.10.2
os: win32 x64
@angular/common: 4.1.3
@angular/compiler: 4.1.3
@angular/core: 4.1.3
@angular/forms: 4.1.3
@angular/http: 4.1.3
@angular/platform-browser: 4.1.3
@angular/platform-browser-dynamic: 4.1.3
@angular/router: 4.1.3
@angular/cli: 1.0.4
@angular/compiler-cli: 4.1.3

The worst thing is that this is a solved problem in ts-loader. I understand the desire for higher performance (transpileModule _is_ faster) but at least add an option for us to use the slower but correct version.

As a temporary workaround you can actually import const enums explicitly and it will work as expected. The problem is that most tools generate definition - and not regular - TS files.

Indeed.

As for the workaround, I agree that explicit importing should work for cases where I we modify the source file. AFAIK we would have to modify that enums file to start with exportmodule enums .... Is there a way to explicitly import a file which does not export?

This is the crux of the issue with Typelite. The enum file generated by Typelite has a structure just like in my steps. I believe I can not explicitly import a Typelite enum since it is generated and, as far as I can tell, there is no way to alter how the module is declared when it is being generated.

We want to use CLI in out current project for better updateability, but could not switch due to the enum problem.
We also use Typlite to generate our enums.

Is there anybody investigating this problem and can say when the problem will be solved?

@lordzero83 I found the reason: https://github.com/angular/angular-cli/issues/2741#issuecomment-294696150

It's need for performance. Each ts file compiles separately, without information from other files. While transpileModule is used, the trouble stays.

The thing is, ts-loader with its fork-checker-plugin beats awesome-typescript-loader in some of our projects and it actually generates correct code.

@KhodeN it would be great if the cli could provide a configuration parameter, where it ignores the performance improvements so that you can use the cli also with const enums (TypeLite) and a slower performance would be ok for me.

This is a major issue for us, as well. The fact that it's a runtime error makes it even worse because it's really easy to miss. @hinaria , what webpack plugin are you using to do the replacement?

This seems to still be an issue but in a different way, and it's causing issues in other module repos: https://github.com/swimlane/ngx-datatable/issues/927

With CLI 1.5+ and Angular 5, the use of transpileModule has been eliminated. Note that Angular 4 uses the previous compiler pipeline so if const enum support is required please update to Angular 5+.

Tested this out and it works! Thanks for fixing it 馃憤

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

_This action has been performed automatically by a bot._

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rajjejosefsson picture rajjejosefsson  路  3Comments

hartjo picture hartjo  路  3Comments

NCC1701M picture NCC1701M  路  3Comments

gotschmarcel picture gotschmarcel  路  3Comments

hareeshav picture hareeshav  路  3Comments