TypeScript: how to check that argument implements interface in JavaScript version

Radek Anuszewski
5 min readMay 16, 2016

--

Need of strict type checking in dynamic-typed programming languages

Sometimes we have to be sure

I spent almost three years on Python and JavaScript, and I almost never touch any Java line of code, and not Python nor JavaScript are statically typed. And I used to this. But when project gets larger and larger, it becomes very hard to manage when you don’t know what is argument type or what API provides an argument got in constructor. In some cases, it can be figured out based on argument name, in come cases there is a need to debug code, which could be hard thing to do — you have to plug real device to your PC, you have to push application to specified state etc.. It’s much easier if you have set of unit tests, you just go to tests and see what arguments are, but every project has unexplored places, and if eventually you have to touch them it may be a torment. Sometimes I have to be sure, from IDE level, what API object exposes to me, what interface it has. Because of fact, that Python and JavaScript don’t have interfaces, in my company we have to mock it.

Mocking interfaces in Python

In Python, me made a class which methods throw NotImplementedError:

class Device: 
def name(self):
raise NotImplementedError(self.__class__.__name__)
class DeviceImpl(Device):
# constructor etc..
def name():
return self.__name

And then we can assert on it to make sure that argument always extend Device:

class DeviceGroup: 
def __init__(device):
# assert for development purposes
assert isinstance(device, Device)
# or do something else when got argument with bad
# type in production
if not isinstance(device, Device):
device = NullObjectDevice()
self.__device = device

In code above Null Object design pattern is used, it helps to avoid checking if object is None before calling action on it.

Mocking interfaces in JavaScript

And it looks pretty much the same like in Python, we define prototype which other prototypes can extend:

/**
*
* @interface
*/
function Device(){}
Device.prototype.getName = function(
throw new Error('Not implemented!');
);
DeviceImpl.prototype = Object.create(Device.Prototype);/**
*
* @implements Device
*/
function DeviceImpl(){
Device.call(this);
}
// Somewhere in code
if ( !(device instanceof Device) ) {
throw new TypeError('Bad interface!');
}

Note, that in code above @interface and @implements from JSDoc are used. It could really help in everyday work — with it IDE can, for example, highlight class when not implement every methods from “interface” or when signature are mismatched.

Typescript as incomplete savior

Quick note

In TypeScript web page we have Playground, which gives us possibility to play with it and see instant compilation result. What is important, when we click blue “Share” button on left side, our code is now in page URL and we can share this code with others — and this method I will use in further examples. And what really should be mentioned — we have code assistance while playing in Playground. And I think it’s only a little taste of how IDEs can support us while writing code in TS. It’s a revolution if we juxtapose it with today’s JS support in IDEs.

Interfaces in TypeScript

TypeScript has nice concept of interfaces. Interface can be implemented explicitly (here is link to TS playground, based on their example: Explicit implementation of interface in TS full code):

interface IGreeter {
˛ greet();
}
class Greeter implements IGreeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return “Hello, “ + this.greeting;
}
}
#other stuff

Or it can be implemented implicitly (Playground: Implicit implementation on interface in TS full code):

interface IGreeter {
greet();
}
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return “Hello, “ + this.greeting;
}
}
#other stuff

Which is in my opinion one of the nicest thing about TS, cause it means that object literals (very popular in AngularJS is Module Pattern (here by John Papa)). Here’s an example:

interface IGreeter {
greet();
}
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return “Hello, “ + this.greeting;
}
}
let objectLiteralGreeter = {
greet: function(greeter) {
return ‘Hello from object literal!’;
}
}
#other stuff

To see that it really works, check Playground: object literal implicitly implements required interface. Playground does not show any errors. But here is one problem — still type can’t be checked at runtime. And also, sometimes just you have to trust it, like when Angular injects dependencies — for example, it code like this:

DeviceController.$inject: ['DeviceService'];
function DeviceController(DeviceService: IDeviceService) {
}

You can’t be sure what is injected until you assert against it in some way:

DeviceController.$inject: ['DeviceService'];
function DeviceController(DeviceService) {
if (!(DeviceService instanceof IDeviceService)) {
throw new Error('Bad argument type');
}
}

Abstract classes as solution to this problem

We used to use instanceof operator in JS code, we can’t do the same with TypeScript interfaces. But few days ago I read Looking at Abstract Classes and Methods in TypeScript article by Paul Galvin and it opened my eyes. For TypeScript code completion and compile-time checking, we use interfaces as in examples above. But for compile-time type checking, we can use abstract class — interfaces are gone after traspilation/compilation, but abstract classes are still regular classes:

interface IGreeter {
greet();
}
abstract class BaseGreeter implements IGreeter {
greet(){
throw new Error();
}
}
class Greeter extends BaseGreeter {
greeting: string;
constructor(message: string) {
super();
this.greeting = message;
}
greet() {
return “Hello, “ + this.greeting;
}
}

And we can use instanceof, exactly how we used to do it in plain Javascript:

function sayHello(greeter: IGreeter) {
if (!(greeter instanceof BaseGreeter)) {
alert(‘Something went wrong…’);
}
else {
alert(greeter.greet());
}
}

Working example is here: Playground: using an abstract base class to make instanceof useful again.

Conclusion

When we rewrite our old code base to TypeScript, we don’t have to resign from old style type checking with JS. There are situations when TypeScript is only code completion tool, like AngularJS’s dependency injection. With a little effort we can create two-level of type checking:

  1. Before compilation to JS, with interfaces
  2. After compilation to JS, with abstract base classes implementing interfaces

And I am sure that this fact would make migration from JS to TS easier for us, and less frightening for our executives.

--

--

Radek Anuszewski

Software developer, frontend developer in AltConnect.pl, mostly playing with ReactJS, AngularJS and, recently, BackboneJS / MarionetteJS.