microsoft/TypeScript

Const enums are preserved when using transpileModule

mbardauskas opened this issue · 3 comments

It seems that using tsc from command line and calling ts.transpileModule in node provides different results: transpileModule preserves const enums. Whole reproduction source can be found here: https://github.com/mbardauskas/transpile-repro

file enums.d.ts:

declare const enum FooType {
    Foo = 1,
    Bar = 2,
}

file foo.ts:

/// <reference path="enums.d.ts" />

interface IFoo {
    foo: FooType;
}

class FooClass {
    public type: FooType;
    constructor(obj: IFoo) {
        this.type = obj.foo;
    }
}

export function test() {
    var foo = new FooClass(<IFoo>{
        foo: FooType.Foo
    });

    console.log(foo.type);
}

Using typescript transpileModule to compile file source

var transpiled = ts.transpileModule(inputSource, {
    reportDiagnostics: true,
    compilerOptions: {
        target: ts.ScriptTarget.ES5,
        module: ts.ModuleKind.CommonJS,
        preserveConstEnums: false
    }
});

Output from tsc:

/// <reference path="enums.d.ts" />
var FooClass = (function () {
    function FooClass(obj) {
        this.type = obj.foo;
    }
    return FooClass;
})();
function test() {
    var foo = new FooClass({
        foo: 1 /* Foo */
    });
    console.log(foo.type);
}
exports.test = test;

Output from transpileModule:

/// <reference path="enums.d.ts" />
var FooClass = (function () {
    function FooClass(obj) {
        this.type = obj.foo;
    }
    return FooClass;
})();
function test() {
    var foo = new FooClass({
        foo: FooType.Foo
    });
    console.log(foo.type);
}
exports.test = test;

this behavior is by design. transpileModule generally operates on the source code of the single file (in your case foo.ts) and have not access to other files (enums.d.ts). Because of that for this scenario we don't use type-directed emit because in most cases compiler does not have complete information on hands. I.e. in your case compiler have no idea what is the type of FooType and that FooType.Foo` is actually a const enum and its value should be inlined.

More specifically in transpileModule:

  • imports\exports are not elided just because they cannot be resolved (since we have code for only one file)
  • const enums are treated as regular enums (even for locally defined const enums)
  • files are always emitted as modules even if they don't have top level export \ import \ require

Is there a way then to pass d.ts files or source string for transpileModule so the const enums would get inlined?

If multiple files are compiled at once how it is different from normal compilation? You can use minimal compiler sample from the Wiki to see how it can be achieved using compiler API