TypeScript Ergonomic Brand Checks
Posted on: February 4, 2022
In short, the term "Ergonomic Brand Checks" is linked to branding. Branding is a way to dynamically check for a type. It comes with JavaScript being schemaless. In JavaScript, you need to check for a field/property that exist on an object to ensure you are manipulating an object desired. For example, if you have an array of different objects, you might want to check if you are modifying a Person
or an Animal
before accessing (read or write) the object.
With "Ergonomic Brand Checks" we are especially properties of an object. What is worth talking about is that TypeScript 4.5 allows checking private fields using the in
operator.
1class Person {2 #shortname: string;3 public constructor(name: string) {4 this.#shortname = name;5 }67 public static isPerson(obj: unknown): unknown {8 return obj &&9 typeof obj === "object" &&10 #shortname in obj;11 }12}1314class Animal {15 #shortname: string;16 public constructor(name: string) {17 this.#shortname = name;18 }19}2021const x1 = { shortname: "Test" };22const x2 = new Person("Test2");23const x3 = new Animal("Test3");24console.log(Person.isPerson(x1)); // False25console.log(Person.isPerson(x2)); // True26console.log(Person.isPerson(x3)); // False
Line 10 highlights and presents the newest addition for ergonomic brand check-in TypeScript. The in
operator within the static function of a class is used to check if a private field exists. Oddly, the function does not return a boolean but unknown
. Changing the parameter type to any
gives an output of any
. However, the output is true
or false
when executing the code.
An interesting observation is that the last line returns false
when we pass an instance of the class Animal
even though the Person
and Animal
classes share the same field name. Actually, the value is also not relevant. The previous example modified to have the static function relying on a field that is not used but defined.
1class Person {2 #brand: unknown;3 #shortname: string;4 public constructor(name: string) {5 this.#shortname = name;6 }78 public static isPerson(obj: unknown): unknown {9 return obj &&10 typeof obj === "object" &&11 #brand in obj;12 }13}1415class Animal {16 #brand: unknown;17 #shortname: string;18 public constructor(name: string) {19 this.#shortname = name;20 }21}2223const x1 = { shortname: "Test" };24const x2 = new Person("Test2");25const x3 = new Animal("Test3");26console.log(Person.isPerson(x1)); // False27console.log(Person.isPerson(x2)); // True28console.log(Person.isPerson(x3)); // False
The trick of using unsused private property has been there for a while. What is new is the possibility to use the in
. I'll conclude by referring to a great article by Michal Zalecki about four ways to nominal typing techniques in TypeScript.