What is an interface?
Google defines an interface as “a point where two systems, subjects, organisations, etc. meet and interact,” and this definition holds true for interfaces in programming. In software development, an interface is a structure that enforces specific properties on an object — in most languages this object is a class.
Here’s an example of an interface in Java:
In the above example, the
Car interface describes a class that has two methods with no return type, both of which take a single integer argument. The details of the implementation of each function is left up to the class, this is why the methods both have no body. To ensure a class implements the
Car interface, we use the
class keyword, but in truth this is just an uninstantiated constructor function, and once called it is simply an object. Objects are ubiquitous, so it is sometimes beneficial to ensure they match a specific shape.
Recently at work I found a case where a developer was expecting a property returned as part of an API response to be
true but instead got
"true", causing a bug. An easy mistake, and one that could also have been avoided if we had an interface.
But wait, there’s more!
Interfaces, with a few minor modifications, could be used to reshape objects. Imagine implementing a “strict” interface, where no properties outside of the interface are permitted, we could delete or rename these properties, or even throw an error if we encounter them.
So now we have an interface that will tell us when we are missing certain properties, but also when we have unexpected properties, or if the properties’ types are not what we expect. This adds other possibilities, say for example refactoring a response from an API while adding that extra layer of safety on top of the interface’s standard behaviour. We could also use interfaces in unit tests if we have them throw errors.
First install the package:
npm install implement-js
Next, create a .js file and import
Our first interface
To create an interface, simply call
Interface and pass in a string as the name of your interface — it’s not recommended, but if you omit the name a unique ID will generated. This returns a function that accepts an object where the properties are all
type objects, a second argument can also be passed with options to show warnings, throw errors, delete or rename properties, ensure only the properties of the interface are present, or to extend an existing
Here’s an interface that describes a car:
It has a
seats property that should be of type number, a passengers array that contains objects which must themselves implement the
Passenger interface, and it contains a
beep property, which should be a function. The
strict options have been set to true, meaning that errors will be thrown when a property of the interface is missing and also when a property not on the interface is found.
Implementing our interface
Now we want to implement our interface, in a simple example we will create an object using an object literal and see if we are able to implement our interface.
First, we create a
Ford object, then we will attempt to implement it against our
As we see from the comment above, this throws an error. Let’s look back at our
We can see that while all the properties are present, strict mode is also true, meaning that the additional property
fuelType causes an error to be thrown. Additionally, although we have a
passengers property, it is not an array.
In order to correctly implement the interface, we remove
fuelType and change the value of
passengers so that it is an array containing objects that implement the
For example, using
implement-js we can easily refactor an API response while ensuring it has not deviated from what we expect. Here’s an example used in conjunction with
First, we define the
TwitterUser interface, which extends the
User interface, as an object with
trim is true meaning that we will discard any properties not described on the
TwitterUser interface. Since our API returns properties in an unfriendly format, we have renamed the properties from
twitter_id to camelcase versions of themselves.
Next, we define an async action with
redux-thunk, the action triggers an API call, and we use our
TwitterUser interface to discard properties we don’t want and to ensure it implements the properties we expect, with the correct types. If you prefer to keep your action creators pure(er) or don’t use
redux-thunk, you may want to check the interface inside
twitterService.getUser and return the result.
Note: when extending an interface options are not inherited
Unit tests are also a suitable place to use interfaces:
implement-js we have also been able to gain additional utility on top of the added security.