Search code examples
javascripttypescriptecmascript-5iife

Why does TypeScript pack a class in an IIFE?


Here is a TypeScript class:

class Greeter {
    public static what(): string {
        return "Greater";
    }

    public subject: string;

    constructor(subject: string) {
        this.subject = subject;
    }

    public greet(): string {
        return "Hello, " + this.subject;
    }
}

It is transpiled to IIFE when TS targets ES5:

var Greeter = /** @class */ (function () {
    function Greeter(subject) {
        this.subject = subject;
    }
    Greeter.what = function () {
        return "Greater";
    };
    Greeter.prototype.greet = function () {
        return "Hello, " + this.subject;
    };
    return Greeter;
}());

However, it generally works in the same way when it is presented as a constructor function. Which, of course, looks more JavaScriptish and handwritten :)

function Greeter(subject) {
    this.subject = subject;
}
Greeter.what = function () {
    return "Greater";
};
Greeter.prototype.greet = function () {
    return "Hello, " + this.subject;
};

Usage:

Both blocks of code work in the same way:

Greater.what();  // -> "Greater"
var greater = new Greater("World!");
greater.greet(); // -> "Hello, World!

What is the benefit or motives to pack it in IIFE?

I made a naive benchmark:

console.time("Greeter");
for(let i = 0; i < 100000000; i++) {
    new Greeter("world" + i);
}
console.timeEnd("Greeter");

It showed virtually the same instantiation speed. Of course, we cannot expect any difference, because the IIFE is resolved only once.

I was thinking that maybe it is because of closure, but the IIFE doesn't take arguments. It must not be a closure.


Solution

  • TypeScript will pass arguments to the IIFE in cases where there is inheritance between classes. For example, the closure below is used when Greeter extends a BaseGreeter class:

    var Greeter = /** @class */ (function (_super) {
        // __extends is added by the TS transpiler to simulate inheritance
        __extends(Greeter, _super);
        function Greeter(subject) {
            var _this = _super.call(this) || this;
            _this.subject = subject;
            return _this;
        }
        Greeter.What = function () {
            return "Greater";
        };
        Greeter.prototype.greet = function () {
            return "Hello, " + this.subject;
        };
        return Greeter;
    }(BaseGreeter));