True Compile/Run-time privacy in WebComponents with TypeScript 2.7

Martin Hochel
4 min readFeb 26, 2018

--

Hey everyone! As you’ve may noticed, TypeScript 2.7 was released not long time ago and it brings a lot of new features to the table.

In this article we will focus primarily on Unique Symbol Types support, in particular within ES2015 classes, which are also a primary building block for WebComponents.

For our demonstration purpose, we will implement a simple WebComponent for showing/hiding projected content, with following features:

  • custom element name: my-toggleable
  • it is both stateful and controllable component
  • public API contains 2 props: title and show and 1 default content projection via slot .
  • title is gonna render within shadow-dom
  • show will toggle the content projection visibility from inside the component, or from parent, by setting values to this property ( controlled component pattern )
  • on host click, it will toggle visibility of projected content

Usage:

<my-toggleable>
<p>Some text Lorem ipsum dolor sit amet.</p>
</my-toggleable>

Demo:

Demo

Ok lets implement our Toggleable WebComponent:

As we know, standard pattern to implement Custom Element props is following:

  • internal “private” class property which keeps current property value
  • getter to that “private” property for public use
  • setter for “private” property for public use

So let’s say for our title prop, following needs to be implemented

class Toggleable extends HTMLElement {
private _title = 'No title provided'
set title(value: string) {
this._title = value
this.render()
}
get title() {
return this._title
}
}

Because we are using TypeScript, we can annotate our internal property with private accessor.

Whole Custom Element implementation, with various private accessors

Whole Toggleable Custom Element implementation, with various private accessors

While this will indeed mitigate the direct access during compile time, it will be still accessible/present in the runtime, and because of that, it's not truly private!

Whoops!

Privacy during compile time

Privacy is gone during run time

Now what ???

Well there are private properties coming soon to EcmaScript classes but we are not there yet…

Until then we can use Symbol primitive type in combination with class/object dynamic property definition. This technique wasn't possible before with TypeScript, thankfully TS 2.7 adds this feature.

First let’s define our private symbols for all private properties of our class:

const titleProp = Symbol('title')
const showProp = Symbol('show')
const viewProp = Symbol('view')
const toggle = Symbol('toggle')

Now our title CustomElement API definition will look following:

class Toggleable extends HTMLElement {
[titleProp] = 'No title provided'
set title(value: string) {
this[titleProp] = value
this.render()
}
get title() {
return this[titleProp]
}
}

Whole Custom Element implementation, with true compile and runtime privacy for particular class properties

Whole custom element implementation, with true compile and runtime privacy for particular props

Privacy during compile time

Privacy during run time

Note:

As we saw in the gif, you cannot create identical symbol, by yourself by naively creating Symbol with same string as in our implementation, and access the class dynamic property, because Symbols are always unique!

Only possible way how to access our private properties is to export those symbols and use them as a reference.

But in real life, there isn’t a valid use case to do that, so please don’t ;)

Nice, isn’t it ?

Stay type-safe and truly private within your WebComponent or Class implementations thanks to latest additions to TypeScript 2.7

As always, don’t hesitate to ping me if you have any questions here or on twitter (my handle @martin_hotell) and besides that, happy type checking folks and ‘till next time! Cheers!

--

--

Martin Hochel

Principal Engineer | Google Dev Expert/Microsoft MVP | @ngPartyCz founder | Speaker | Trainer. I 🛹, 🏄‍♂️, 🏂, wake, ⚾️& #oss #js #ts