Qubyte Codes

Interfaces for JavaScript

Published

I use instanceof a lot in JavaScript. It's very handy when writing unit tests. It's easier to do an instanceof check than it is to exhaustively probe an object.

Unfortunately instanceof usually means an object has been constructed. If the constructed object is coming from a third party library, or there is no access to the constructor, it can become fiddly.

This is why I'm excited about ES2015s Symbol.hasInstance. It allows you to tune the behaviour of instanceof for a class. Here's a minimal example of an interface class:

class PositiveInteger {
  constructor() {
    throw new Error('PositiveInteger is an interface class.');
  }

  static [Symbol.hasInstance](value) {
    if (typeof value !== 'number') {
      return false;
    }

    if (value < 0) {
      return false;
    }

    return value % 1 === 0;
  }
}

assert.ok(10 instanceof PositiveInteger);   // does not throw
assert.ok(-10 instanceof PositiveInteger);  // throws
assert.ok('hi' instanceof PositiveInteger); // throws
var positiveInt = new PositiveInteger();    // throws

The class above exists only to provide this instanceof check. A more interesting example might be a view. I assert that a view has an element, and render and remove methods. An interface class for this might be:

class View {
  constructor() {
    throw new Error('View is an interface class.');
  }

  static [Symbol.hasInstance](value) {
    if (!value) {
      return false;
    }

    if (typeof value.render !== 'function') {
      return false;
    }

    if (typeof value.remove !== 'function') {
      return false;
    }

    return value.element instanceof HTMLElement;
  }
}

Now view objects can come from any source as long as they have render and remove methods and an element. Objects which implement various interface classes also mean that these interface classes don't have to be in the same prototype chain. In other words, it gives you a way to use instanceof without invoking inheritance.

Sadly hasInstance will be one of the last ES2015 features to make it into browsers, so we'll have to wait a while before we can use it. See the compatibility table.


Feel like sharing or responding?

Comments or corrections? Toot to me or skeet me!

This blog supports webmentions. If your blog supports webmentions and you've linked to this post, then a mention will have been dispatched to me. These are viewed and published manually, so please allow some time. If you would like to manually send me a webmention, please submit the URL to it here:

Edit page.