I was reading the release notes for the TypeScript 3.8 beta the other day and there’s a particular feature in there that caught my eye, Private Fields. This is support for the stage 3 proposal which means it’s a candidate for inclusion in a future language version (more info on the stages can be found here).
What I found interesting is that although TypeScript has supported a private
keyword it doesn’t actually make the field private, it just tells the compiler, meaning that in “plain old JavaScript” you can still access the field, whereas the Private Fields implementation makes it properly truly private, you can’t access it. So how does TypeScript do this while still generating valid JavaScript? This was something I wanted to learn.
The easiest way to figure this out is to look at the generated JavaScript from the TypeScript compiler, so let’s start with the sample from the blog post:
|
|
You’ll see the new syntax in the #name
field that indicates it’s a private field. If we pass this through the compiler we’ll get this:
|
|
We’ll come back to the generated functions __classPrivateFieldSet
and __classPrivateFieldGet
shortly, let’s first look at the class:
|
|
Notice there’s a variable generated called _name
that is an instance of a WeakMap
. The WeakMap
type in JavaScript is a special kind of key/value store that uses objects as the key, and we can see that in the constructor
it calls _name.set(this, void 0);
, so it’s initialising the value in the store to void 0
(which is a fancy way to write undefined
). Now, if we were to give the field an initial value like this:
|
|
It’s change the generated code to use _name.set(this, "");
. Next it uses one of the generated functions, __classPrivateFieldSet
, which does what you’d guess from the name, sets the value in the WeakMap
for the current instance of the class to the value provided (it does some error checking too). Then when we want to access the value the __classPrivateFieldGet
function is used to get the value back out of the WeakMap
that contains it.
Something I also noticed when playing around is that if you were to add another private field:
|
|
The generated code now looks like this:
|
|
We’ve got two WeakMap
’s, one for each of the fields.
Summary
TypeScripts use of the WeakMap
and the instance of the class as the key is quite an ingenious one when it comes to doing private fields for a class, but I do wonder what the trade off would be in memory consumption, since every class will name n number of WeakMap
instances, and do they take up much memory to the point it could be impactful?
None the less it does give me ideas for when I’m building applications and I want to have restricted access to parts of a type, using a WeakMap
as a store might just do the trick.