Published on

Typescript - Are “Private” properties private?

Authors

NB: I published this article originaly at medium. I decided to move it to my personal blog since I have had one ;)

If you are reading this article, I guess you are interested in the Typescript language. Either you are a newbie or an experienced developer in Typescript language, I bet you use the conceptprivate for class properties many times. But do you know that it is not private at all. Let’s discover together.

Take a look at an example where I have a Student class:

export default class Student {
  name: string
  email: string
  private phone: string

  constructor(name: string, email: string, phone: string) {
    this.name = name
    this.email = email
    this.phone = phone
  }

  get4FirstNumbersOfPhone() {
    return this.phone.substring(0, 4)
  }

  private playGame() {
    // Private time to play game (^-^)
  }
}

As you can see there are two private properties in the class: attribute phone and method playGame() and we expect that the outside of the class cannot call these properties. And yes, the Typescript compiler guarantees the rule for us.

In the index.js I import the class and I try to violate the rule:

import Student from './Student'

function main() {
  const student = new Student('John Doe', 'john@abc.com', '012345678')

  student.playGame()

  console.log(student.phone)
}

main()

With the modern IDEs and useful plugins, developers can be alerted if they do like this. How luckily we are, right!

But, even you use a simple text editor to write these lines of code. The Typescript compiler will throw errors when building:

src/index.ts:6:11 - error TS2341: Property 'playGame' is private and only accessible within class 'Student'.

6   student.playGame();
            ~~~~~~~~

src/index.ts:8:23 - error TS2341: Property 'phone' is private and only accessible within class 'Student'.

8   console.log(student.phone);
                        ~~~~~


Found 2 errors.

error Command failed with exit code 2.

You can see the error messages. So, are we safe? The answer is NOT YET. Unfortunately, Typescript only alerts us during compile time, after that, the compiled result is javascript where these private properties will be fully accessible and visible. Let see how:

import Student from './Student'

function main() {
  const student = new Student('John Doe', 'john@abc.com', '012345678')

  ;(student as any).playGame()

  console.log((student as any).phone)
}

main()

So, I can violate the rule by cast the Student instance as any and this is the compiled result:

'use strict'
var __importDefault =
  (this && this.__importDefault) ||
  function (mod) {
    return mod && mod.__esModule ? mod : { default: mod }
  }
Object.defineProperty(exports, '__esModule', { value: true })
const Student_1 = __importDefault(require('./Student'))
function main() {
  const student = new Student_1.default('John Doe', 'john@abc.com', '012345678')
  student.playGame()
  console.log(student.phone)
}
main()

Then, in runtime, it works. Of course, use any is a bad practice or even a mistake. My point here is to prove that we can call private properties in runtime from outside of the class as we have known about Javascript.

But you can have realprivate properties

With private lass fields, we can easily achieve real private properties. There is however a stage 3 proposal to allow defining private class fields using a hash # prefix. I will modify the class Student as below:

export default class Student {
  name: string
  email: string
  #phone: string

  constructor(name: string, email: string, phone: string) {
    this.name = name
    this.email = email
    this.#phone = phone
  }

  get4FirstNumbersOfPhone() {
    return this.#phone.substring(0, 4)
  }

  #playGame() {
    // Private time to play game (^-^)
  }
}

You can see now, the attribute phone is changed to #phone and the methodplayGame() is changed to #playGame. I keep the same main file with the trick student as any and you can see the result when compiling the code:

src/Student.ts:16:3 - error TS18022: A method cannot be named with a private identifier.

16   #playGame() {
     ~~~~~~~~~


Found 1 error.

error Command failed with exit code 2.

The compiler throws an error and the compiling process is exited at the first line of code that the rule is violated. We can see it is more restricted than before where it throws errors for all of the violated lines of code.

You can check more examples in private class fields documentation.


That’s it! I hope you have found a cool knowledge of Typescript. If you have other opinions, please let me know by leaving the comment below.

Keep calm and enjoy programming!

If you like the post, you can buy me a Coffee at https://ko-fi.com/tlcong.