s-panferov / awesome-typescript-loader

Awesome TypeScript loader for webpack
Other
2.35k stars 179 forks source link

Const enum are preserved and not replaced with actual values #137

Open galtalmor opened 8 years ago

galtalmor commented 8 years ago

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);
ThaNarie commented 8 years ago

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

alexeib commented 8 years ago

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

s-panferov commented 8 years ago

Please try disableFastEmit option. Will it work?

alexeib commented 8 years ago

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

galtalmor commented 8 years ago

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

ZenSoftware commented 8 years ago

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

vaceslav commented 7 years ago

Any update / workaround for this issue?

ZenSoftware commented 7 years ago

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.

vaceslav commented 7 years ago

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

ZenSoftware commented 7 years ago

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.

vaceslav commented 7 years ago

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.

ZenSoftware commented 7 years ago

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.

vaceslav commented 7 years ago

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

squadwuschel commented 7 years ago

any news here or solutions for this problem?

roxteddy commented 7 years ago

It is blocking me as well. Any news ?