Search code examples
javascripttypescripttsc

Questions about typescript compile


I have two questions about ts.

uncompiled code

class K {
  private p = 3;
  public q = 4;
  public r = () => {
    console.log(this.p, this.q);
  };
}

new K().r();

compiled code

var K = /** @class */ (function() {
  function K() {
    var _this = this;
    this.p = 3;
    this.q = 4;
    this.r = function() {
      console.log(_this.p, _this.q);
    };
  }
  return K;
})();
new K().r();

1. Why private variables become public after compilation?

I thought the code would be something like below to make q private. Why TS does not do it?

var K = /** @class */ (function() {
  var p = 3;
  function K() {
    var _this = this;
    this.q = 4;
    this.r = function() {
      console.log(p, _this.q);
    };
  }
  return K;
})();
new K().r();

2. What is the role of wrapping anonymous function?

If it does not use module pattern, anonymous function seems to be useless. The code could be like below I think.

var K = function() {
  var _this = this;
  this.p = 3;
  this.q = 4;
  this.r = function() {
    console.log(_this.p, _this.q);
  };
};
new K().r();


Solution

  • Why private variables become public after compilation?

    The idea of private properties directly on an instance is a very new one (using # syntax). There is no such keyword as private or public in actual Javascript. Declaring a property private or public in Typescript is there to help you and others who read and use the code understand how the code is meant to be used, but it doesn't make it required.

    For similar reasons, you can write

    window.fn = (arg: string) => {
      console.log(arg.slice(5));
    };
    

    and then, outside of the project, in Javascript, write

    window.fn(5);
    

    despite the fact that fn only accepts a string.

    If you want to make the properties actually inaccessible from the outside, use #, eg:

    class K {
      #p = 3;
    

    This syntax is extremely new in Javascript, and is only supported in Typescript 3.8+. Eg

    class K {
      #p = 3;
      public q = 4;
      public r = () => {
        console.log(this.#p, this.q);
      };
    }
    
    new K().r();
    

    compiles to

    "use strict";
    var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, privateMap) {
        if (!privateMap.has(receiver)) {
            throw new TypeError("attempted to get private field on non-instance");
        }
        return privateMap.get(receiver);
    };
    var _p;
    var K = /** @class */ (function () {
        function K() {
            var _this = this;
            _p.set(this, 3);
            this.q = 4;
            this.r = function () {
                console.log(__classPrivateFieldGet(_this, _p), _this.q);
            };
        }
        return K;
    }());
    _p = new WeakMap();
    new K().r();
    
    1. What is the role of wrapping anonymous function?

    The whole creation of the class is encapsulated in an IIFE. Sometimes, this doesn't seem to do anything, but with some types of transpiled code, it helps prevent unnecessary scope leakage. Eg

    class K {
      #p = 3;
      static foo = 'bar'
      method() {
    
      }
    }
    

    transpiles to

    "use strict";
    var K = /** @class */ (function () {
        function K() {
            _p.set(this, 3);
        }
        K.prototype.method = function () {
        };
        var _p;
        _p = new WeakMap();
        K.foo = 'bar';
        return K;
    }());