Connor Smyth

text

Implementing Interfaces in JavaScript

What are interfaces?

Object interfaces allow you to create code that specifies which methods a class must implement, without having to define how these methods are implemented. Interfaces share a namespace with classes and traits, so they may not use the same name.

In practice, interfaces serve two complementary purposes:

  • To allow developers to create objects of different classes that may be used interchangeably because they implement the same interface or interfaces. A common example is multiple database access services, multiple payment gateways, or different caching strategies. Different implementations may be swapped out without requiring any changes to the code that uses them.
  • To allow a function or method to accept and operate on a parameter that conforms to an interface, while not caring what else the object may do or how it is implemented. These interfaces are often named like IterableCacheableRenderable, or so on to describe the significance of the behavior.

https://www.php.net/manual/en/language.oop5.interfaces.php

Does JavaScript have interfaces?

There’s no notion of “this class must have these functions” (that is, no interfaces per se), because:

  1. JavaScript inheritance is based on objects, not classes. That’s not a big deal until you realize:
  2. JavaScript is an extremely dynamically typed language — you can create an object with the proper methods, which would make it conform to the interface, and then undefine all the stuff that made it conform. It’d be so easy to subvert the type system — even accidentally! — that it wouldn’t be worth it to try and make a type system in the first place.

Instead, JavaScript uses what’s called duck typing. (If it walks like a duck, and quacks like a duck, as far as JS cares, it’s a duck.) If your object has quack(), walk(), and fly() methods, code can use it wherever it expects an object that can walk(), quack(), and fly(), without requiring the implementation of some “Duckable” interface. The interface is exactly the set of functions that the code uses (and the return values from those functions), and with duck typing, you get that for free.

https://stackoverflow.com/a/3710367

Note: JavaScript has the reserved word interface in case they ever would like to implement interfaces

Is Duck Typing enough to replace interfaces all together?

In my personal opinion, no.

Recently, in order to increase my practical JavaScript knowledge, I have been working on both JavaScript and TypeScript projects. The lack of interfaces made it difficult to implement things in JavaScript that I had architected with PHP in mind. I also began, as a practice project, to try to clone certain small PHP packages in JavaScript but regularly ran into the issue that JavaScript lacks interfaces where PHP does not.

This is when I came up with an idea to create my own “interface” in JavaScript.

Creating “interfaces” in JavaScript

The first thing I did was to create a new class with Interface as the suffix in the class name. This will help you differentiate what is an implementation and what is an interface when looking through files. In this case, let’s call our class MyInterface

module.exports = class MyInterface {

}

I then created all the methods that I wished the implementers to implement but created them as empty methods.

module.exports = class MyInterface {

  /**
   * @param {string} var1
   * @param {string} var2
   *
   * @return {string}
   */
  firstMethod (var1, var2) {}

  /**
   * @return {string}
   */
  secondMethod () {}

  /**
   * @param {string} var1
   */
  thirdMethod (var1) {}
}

The next step is to “implement” the interface. What we are really doing is extending the MyInterface class, but if we try to use the methods of the implementation class and we do not implement them, it will cause issues because there will be no actions occurring on the method calls.

const MyInterface = require('./MyInterface')

module.exports = class MyImplementation extends MyInterface {

  myVar

  constructor (theVar)
  {
    this.myVar = theVar
  }

  /**
   * @inheritDoc
   */
  firstMethod (var1, var2)
  {
    return var1 + var2
  }

  /**
   * @inheritDoc
   */
  secondMethod ()
  {
    return 'secondMethod'
  }

  /**
   * @inheritDoc
   */
  thirdMethod (var1)
  {
    this.myVar = var1
  }
}

I know this isn’t a perfect solution, but I see it as good enough for solving the design implementation problems. This will allow you to also set the return type to the interface even when you are implementing. It will also not let you try to instantiate the interface class as there is no constructor defined.

/**
 * {MyInterface} myObject
 */
 const myFunction = () => {
   return new MyImplementation()
 }

I am perfectly aware that there are many ways to work around this and subvert the type system. I only wrote this post so that anyone who wanted to try to introduce interfaces into their JavaScript designs could have a way to do so.