Question

Before I proceed, I would like to point out that I have asked a few questions already regarding TypeScript, its compiler and what it has and has not been able to achieve throughout its life and roadmap towards version 1.0

This question relates to the use of public and private keywords in TypeScript, and how these relate to compiled JavaScript.

Consider the following TypeScript class:

class Example {
    private messageA: string;
    public messageB: string;

    constructor(message?: string) {
        this.messageA = "private: " + message;
        this.messageB = "public: " + message;
    }

    public showMessageA(): void {
        alert(this.messageA);
    }

    private showMessageB(): void {
        alert(this.messageB);
    }
}

var example = new Example("Hello World");

Now, when I type example. intellisense (TypeScript) tells me that I can access messageB, and showMessageA, because they are both public. However, this behavior (whilst possible) is not evident in the compiled JavaScript.

Here is the JavaScript compilation for my class:

var Example = (function () {
    function Example(message) {
        this.messageA = "private: " + message;
        this.messageB = "public: " + message;
    }
    Example.prototype.showMessageA = function () {
        alert(this.messageA);
    };

    Example.prototype.showMessageB = function () {
        alert(this.messageB);
    };
    return Example;
})();

var example = new Example("Hello World");

Now, if I paste this example into my browser console (I'm using Chrome), I can access messageA, messageB, showMessageA, showMessageB which implies that in JavaScript, all access modifiers are ignored.

Personally, I think this is wrong! JavaScript is capable of modelling access modifiers so it is my belief that TypeScript should follow suit.

Consider the following hand written JavaScript, which models the private and public variables and functions correctly:

var Example = (function() {
    return function Example(message) {
        var messageA = "private: " + message;
        this.messageB = "public: " + message;

        this.showMessageA = function() {
            alert(messageA);
        }

        var showMessageB = function() {
            alert(this.messageB);
        }
    }
})();

var example = new Example("Hello World");

Now, if I paste this example into my browser console, I can only access messageB and showMessageA, which in accordance with what I was trying to achieve with TypeScript is correct.

QUESTIONS

  1. Why does the TypeScript compiler ignore access modifiers when compiling to JavaScript?
  2. Why does TypeScript bind all methods to the prototype, rather than on a per-instance basis?
  3. If there is anything favorable about the way TypeScript compiles classes in comparison to my custom implementation, what is it, and why?
Was it helpful?

Solution

The problem with using a closure to imitate private access is that every instance needs its own copy of each method. This means that every time you create an instance, each method function has to be compiled and space in memory has to be reserved for the new function. That is not ideal, and not what TypeScript is trying to achieve.

OTHER TIPS

Semantically, the methods-as-closures model you describe is, in many ways, superior to JavaScript's model of putting methods on the prototype -- even for public methods. And some people on the EcmaScript 6 committee (on whose class system TypeScript is based), would have preferred a class system along these lines. In fact, early internal versions of TypeScript implemented such a model.

Unfortunately, such a model cannot be made efficient in JavaScript. It generally would require that every method is allocated as a separate closure for every object instance -- i.e., creating a single object instance would involve creating many function objects. And with JavaScript's saddeningly weak treatment of this, and its notion of function identity, there is no easy way in which these allocations could be optimised away in general.

Consequently, EcmaScript 6 settled on the current model (though it has only public for now), and TypeScript followed regretfully (for both public and private). Private members then merely provide static checks in TypeScript, with no encapsulation guarantees.

FWIW, the proper way to provide privates in this model would be private symbols (a.k.a. private names), but unfortunately, those didn't make it into ES6, and are no option for TypeScript anyway.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top