The private in TypeScript is kinda private

TypeScript includes the keywords public, protected, and private to control access to the members of a class such as properties or methods. Public class members are visible from within and outside the class, protected are visible form the class and its descendants, and private are visible from within the class only. But being a superset of JavaScript, which doesn’t support the private keyword, using private members in TypeScript is not what you may expect.

Let’s look at the example of the class Person, which has three public and one private property.

class Person {
    public firstName: string;
    public lastName: string;
    public age: number;
    private ssn: string;

    constructor(firstName:string, lastName: string, age: number, ssn: string) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
        this.ssn = ssn;
    }
}

var p = new Person("John", "Smith", 29, "123-90-4567");

console.log("Last name: " + p.lastName + " SSN: " + p.ssn);

The TypeScript compiler properly reports an error at the last line where I tried to access the private variable ssn. Here’s the error:

“Error:(17, 53) TS2341: Property ‘ssn’ is private and only accessible within class ‘Person’.”

But this should have been marked as a warning, because the TypeScript compiler generates the JavaScript code anyway, which looks like this:

var Person = (function () {
    function Person(firstName, lastName, age, ssn) {
        this.firstName = firstName;
        this.lastName;
        this.age = age;
        this.ssn = ssn;
    }
    return Person;
})();

var p = new Person("John", "Smith", 29, "123-90-4567");
console.log("Last name: " + p.lastName + " SSN: " + p.ssn);

To see the code generation in action, visit the TypeScript Playground at http://bit.ly/1GKUeV0. As you see all traces of privacy have been removed, the property ssn has been exposed to the outside world as every other Property on the Person object.

Moreover, there is an easy workaround for accessing the “private” ssn even in TypeScript by casting this property to the type any:

console.log("Last name: " + p.lastName + " SSN: " + (p as any).ssn);

There must be some reason why creators of the TypeScript compiler didn’t turn the class Person into the JavaScript closure that would actually make ssn private, for example:

var Person = (function () {
    var _ssn;
    
    function Person(firstName, lastName, age, ssn) {
      
        this.firstName = firstName;
        this.lastName;
        this.age = age;
        _ssn = ssn;
    }
    return Person;
})();

But it is what it is. So when you use the private keyword in TypeScript, keep in mind that it just suggests you to be nice and not access it directly, but write public getter and setter methods instead.

Let’s extend the class Person like this:

class Employee extends Person{
    ssn: number;
}

TypeScript’s compiler will give you an error:

Error:(17, 53) TS2341: Property ‘ssn’ is private and only accessible within class ‘Person’.

But it’ll still generate the JavaScript code unless you add a compiler option –noEmitOnError to not generate JavaScript until all TypeScript syntax errors are fixed.

Having said that, I still like TypeScript. Developing in TypeScript is clearly more productive than in JavaScript. For more TypeScript articles go here.

P.S. IMO, protected variables are useless in any object–oriented language.

10 thoughts on “The private in TypeScript is kinda private

  1. I don’t understand why “ssn:number” in the class that extends Person gives you an error. Shouldn’t it be possible to have two “ssn”s, one inherited from the base class, and the other one declared in Employee? I understand you can’t access the one from the base class, if it’s private, but shouldn’t the “ssn” from Employee be accessible?

    1. It’s shows the same error in WebStorm as well, but it compiles it anyway into JavaScript. You can save this code in a ts file and compile it with tsc on the command line. You’ll get the error message, but the .js file will be generated anyway.

      1. Just tried again, here is the results:
        Command line: shows error, but anyway compiles to js.
        Visual Studio: shows error and didn’t compile to js, another files in solution also is not compiling until all build errors are fixed.

        Hm, just tried to compile it with command line using flag “–noEmitOnError” and it didn’t compile! For example: “tsc –noEmitOnError app.ts”.
        Looks like Visual Studio uses this flag by default.

  2. Great commentary on Typescript and I like your brutal honesty that once compiled, the JS is exposed, so your property may not be that private! Typescript is a great new compiler for JavaScript and I love the much better debugging capabilities.

  3. I love your P.S. – have the same feeling. Especially in TS using private attributes is kind of awkward for the reason you explained above and also because it makes inheritance a big pain (constructors with different param names for example).

    1. True. The closure is created on the Person function and exists within it. To truly create a private variable on the instances, closures need to be created on the instances, like…

      var Person = (function () {
           
          function Person(firstName, lastName, age, ssn) {
              var p = Object.create(Person.prototype);
              p.firstName = firstName;
              p.lastName = lastName;
              p.age = age;
              p.getSSN = function(){
                  return ssn;
              }
              return p;
          }
      
          Person.prototype.appendToSSN = function(str){
             console.log(this.getSSN() + "_" + str);
          }
          return Person;
      })();
      
      var p1 = new Person("V", "S", 25, "SSN25");
      var p2 = new Person("B", "S", 27, "SSN27");
      
      p1.appendToSSN("p1");
      p2.appendToSSN("p2");
      

      In this example, each instance created has its own private variable(closure) ssn.

Leave a comment