awesome-typescript-loader icon indicating copy to clipboard operation
awesome-typescript-loader copied to clipboard

Const enum are preserved and not replaced with actual values

Open galtalmor opened this issue 8 years ago • 15 comments

There was a change in the behavior of const enum compilation between v0.16.2 and v0.18.0. Before upgrading my const enum values were replaced with the actual value. Now they keep the variable name, which results in me not being able to access them from external module. I tried to change preserveConstEnums in tsconfig.json, but nothing helps.

For example:

    const enum myEnum {
        a = 0,
        b = 1,
        c = 2
    }

    console.log(myEnum.a);

Results in:

        (function (myEnum) {
            myEnum[myEnum["a"] = 0] = "a";
            myEnum[myEnum["b"] = 1] = "b";
            myEnum[myEnum["c"] = 2] = "c";
        })(mModule.myEnum || (mModule.myEnum = {}));
        var myEnum = mModule.myEnum;

        console.log(myEnum.a);

Previously result was:

        console.log(0);

galtalmor avatar May 15 '16 11:05 galtalmor

I had a similar issue with the const enum feature.

First if all (even in 2.x), it never inlines the (const) enum values, so you will end up with:

var CallListenerResult_1 = __webpack_require__(7);
var _callListenerResult = CallListenerResult_1.default.NONE;

When preserveConstEnums is set to false (or left out) in the tsconfig.json, it will strip the export from the enum file (probably it thinks that it's inlined everywhere, so it's not needed), but due to the incorrect inlining behavior, this will result in an error.

Enum:

const enum CallListenerResult {
    NONE = 0,
}

export default CallListenerResult;

Webpack result with preserveConstEnums set to true:

function(module, exports) {
    "use strict";
    var CallListenerResult;

    (function (CallListenerResult) {
        CallListenerResult[CallListenerResult["NONE"] = 0] = "NONE";
    })(CallListenerResult || (CallListenerResult = {}));

    Object.defineProperty(exports, "__esModule", { value: true });
    exports.default = CallListenerResult;
}

Webpack result with preserConstEnums set to false:

function(module, exports) {
    "use strict";
    var CallListenerResult;

    (function (CallListenerResult) {
        CallListenerResult[CallListenerResult["NONE"] = 0] = "NONE";
    })(CallListenerResult || (CallListenerResult = {}));
}

And because there is no export, the CallListenerResult_1.default will throw a runtime error due to CallListenerResult_1 being null.

So basically, if you are using awesome-typescript-loader and (const) enums, you have to set preserveConstEnums to true in your tsconfig.json to make it output valid code.

note: it's a runtime error, so you might not notice it right away

ThaNarie avatar Jul 11 '16 19:07 ThaNarie

still a problem in any version > 0.17 it seems, including 1.1.1. const enums are supposed to be transpiled into their value, but instead are kept as is which causes runtime exceptions. preserveConstEnums doesnt do anything as we are not exporting the enums

alexeib avatar Jul 27 '16 16:07 alexeib

Please try disableFastEmit option. Will it work?

s-panferov avatar Jul 28 '16 05:07 s-panferov

Nope, putting that into tsconfig as below does not work. Tried both 1.1.1 and 0.19.1

"awesomeTypescriptLoaderOptions": { "forkChecker": true, "useBabel": true, "target": "es6", "disableFastEmit": false },

just to be clear i tried both true and false for disableFastEmit

alexeib avatar Jul 29 '16 17:07 alexeib

@s-panferov This still doesn't work in versions later than v0.16.2. Therefor I didn't update the plug in since. Any suggestions?

galtalmor avatar Sep 01 '16 08:09 galtalmor

@s-panferov I have come across this specific bug as well. It is causing me a bit of trouble with the @types/signalr types package. SignalR.ConnectionState is a const enum. Refer to: DefinitelyTyped/signalr/index.d.ts - Line 12

I have tried setting preserveConstEnums to both true and false. The results are the same for both, enums are not being replaced with their representative integer value. Instead, the variable name of the enum is left intact in the transpiled output. Resulting in code that references a non-existent object.

I should mention that I tried out a different TS compiler: gulp-typescript, and it transpiles const enum to the integer values as expected. Unfortunately it doesn't do any sort of advanced bundling like webpack.

Would there happen to exist a work-around to get awesome-typescript-loader to replace const enum with their integer values?

ZenSoftware avatar Nov 02 '16 05:11 ZenSoftware

Any update / workaround for this issue?

vaceslav avatar Dec 28 '16 16:12 vaceslav

I've tried for days to get this to work. Pretty sure there is none. It's unfortunate given how old this issue is that there is not even a hack available. It is a devastating bug after all. Think of how many code bases are incorrectly being transpiled in the wild right now because of this. I know a large portion of the Angular 2 community has been using awesome-typescript-loader.

I hate to advocate a competing loader.... but ts-loader correctly transpiles const enums. The only way we could solve this crippling issue was simply to switch.

ZenSoftware avatar Dec 28 '16 17:12 ZenSoftware

Is it possiblle (how I can ) to change the typescript loader in angular-cli (beta 24)?

vaceslav avatar Dec 28 '16 17:12 vaceslav

Oh geeze... I think I may get in trouble if I start giving directions on how to setup ts-loader here... It is extremely simple though. They drop directly in and out from one another, as they are competing loaders. http://stackoverflow.com/ may have information that you need. I think once you attempt changing it out, you will find it was surprisingly simple.

ZenSoftware avatar Dec 28 '16 17:12 ZenSoftware

I think, I found a solution. Enums should be defined in *.ts file/files. Not in *.d.ts file. Then you have to import your enum.

Example: enum.ts

export enum MyEnum {
   A = 1,
   B,
   C
}

MyComponent.component.ts


import { MyEnum} from "./enum.ts";

var foo = MyEnum.B;
switch(foo){
   case MyEnum.A:
   case MyEnum.B:
   case MyEnum.C:

}

This works with angular-cli beta 24. tsconfig is unchanged.

vaceslav avatar Dec 28 '16 17:12 vaceslav

Interesting. I am glad you found something that works for you in the meantime. Although this does not solve the most crippling aspect of the bug, in that all const enum in any @type you include will not transpile correctly. Since "d.ts" files are the fundamental unit of how typing works in typescript, this is a big problem being that any "npm install @type" will not allow you to use the const enum in their definitions, and may even break the 3rd party library that you include. You really shouldn't try to hack your way around something like this. It is a far better idea to just use a correctly implemented compiler.

ZenSoftware avatar Dec 28 '16 18:12 ZenSoftware

@ZenSoftware all you say is absolutely correct. I migrate now a project from angular2.rc2 to angular 2.3.1 + angualr-cli and for me it is show stopper when I cannot use enums. so I create multiple enums.ts files.

vaceslav avatar Dec 28 '16 18:12 vaceslav

any news here or solutions for this problem?

squadwuschel avatar May 20 '17 21:05 squadwuschel

It is blocking me as well. Any news ?

roxteddy avatar Oct 09 '17 08:10 roxteddy