Why does TypeScript pack a class in an IIFE?

It's done to preserve native class behavior in edge cases like this, where someone tries to use class Greeter before it's defined:

// this is javascript code, not TypeScript

console.log(Greeter.What());

class Greeter {
}

Greeter.What = function What() {
    return "Greater";
}

With native class implementation, this should print ReferenceError: Greeter is not defined.

When transpiled and wrapped in IIFE, the result is close enough: TypeError: Cannot read property 'What' of undefined.

Without IIFE, unwrapped function is hoisted and name Greeter is in scope before it's defined, so the different error is produced: TypeError: Greeter.What is not a function

Note that IIFE is not used to hide private instance or class properties because it's not necessary anyway. When transpiled, instance properties are assigned as properties for this inside the constructor, and static properties are assigned as properties of Greeter object - no variables are created.


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));