This blog post describes a technique for making instanceof applicable to more values (on the right-hand side). Specifically, for primitive values.

Background: typeof vs. instanceof #

In JavaScript, you have to choose when it comes to checking the type of a value. The rough rule of thumb is:

typeof checks if a value is an element of a primitive type: if ( typeof value === 'string' ) ···

checks if a value is an element of a primitive type: instanceof checks if a value is an instance of a class or a constructor function: if (value instanceof Map ) ··· (Additionally, value.constructor and value.constructor.name are occasionally useful.)

This is already less than ideal, because you have to keep the difference between primitive values and objects in mind, which you can often ignore, otherwise.

Alas, a few quirks make things even more complicated:

typeof null is 'object' , not 'null' . This is considered a bug.

is , not . This is considered a bug. typeof distinguishes between objects and functions (which are also objects): > typeof {} 'object' > typeof function () {} 'function' This quirk, combined with the previous quirk means that there is no simple way to check for object-ness via typeof .

distinguishes between objects and functions (which are also objects): This quirk, combined with the previous quirk means that there is no simple way to check for object-ness via . Not all objects are instances of Object : > Object.create(null) instanceof Object false

Enabling instanceof for primitive values #

Given the class PrimitiveNumber , the following code configures for which values x the expression x instanceof PrimitiveNumber returns true . It does so by implementing a static method for PrimitiveNumber whose key is the public symbol Symbol.hasInstance .

class PrimitiveNumber { static [ Symbol .hasInstance](x) { return typeof x === 'number' ; } } console .log( 123 instanceof PrimitiveNumber);

Dynamic type checking via the TypeRight library #

TypeRight is a minimal library for dynamic type checking. Among other features, it uses the approach shown in the previous example to implement the following pseudo-classes. Their only purpose is to be right-hand sides of the instanceof operator:

PrimitiveUndefined

PrimitiveNull

PrimitiveBoolean

PrimitiveNumber

PrimitiveString

PrimitiveSymbol

TypeRight does not currently provide a class for checking if a value is an object, but that could easily be added.

Building on these foundations, you can use TypeRight to check if the parameters of a function have the proper types:

import * as tr from 'type-right' ; function dist ( x, y ) { tr.force(x, tr.PrimitiveNumber, y, tr.PrimitiveNumber); return Math .hypot(x, y); } dist( 3 , 4 ); dist( 3 , undefined );

Other approaches for simplifying type checks #

Two proposals for handling type checks are currently at stage 0. That means that they describe ideas that may or may not be explored further in the future.

Pattern matching #

The proposal “ECMAScript Pattern Matching Syntax”, by Brian Terlson and Sebastian Markbåge, introduces the key Symbol.matches to make values “matchable”:

match (val) { MyClass : console .log( 'val is an instance of MyClass' ); }

The property could either be generated automatically or added manually, as follows (line A).

class PrimitiveNumber { static [ Symbol .matches](x) { return x instanceof this ; } }

The proposal “ Builtin.is and Builtin.typeOf ” by James M. Snell introduces several mechanisms for type checking.

Builtin.is(value1, value2) checks if value1 and value2 refer to the same builtin constructor. It takes into consideration that value1 and value2 may come from the current realm or another realm:

> Builtin.is(Date, vm.runInNewContext('Date')) true > Builtin.is(Date, Date) true

Builtin.typeOf() can be seen as an extension typeof that works for both primitive values and built-in classes:

Builtin.typeOf( undefined ); Builtin.typeOf( null ); Builtin.typeOf( 123 ); Builtin.typeOf( new Number ()); Builtin.typeOf([]); Builtin.typeOf( new Map ()); class MyArray extends Array {} Builtin.typeOf( new MyArray());

Further reading #