Higher Order Classes with Javascript Decorators

Among the chief problems regarding software maintainability are the ones related to code duplication. It makes senses that there are quite the number of language features, design patterns and software principles to give the developers means to keep their projects DRY. By now we’ve learned lots of good and bad ways to do it and this is about one of these good ways that we can use in the modern JavaScript based projects.

I think before going further we can take a look at how did the javascript community handled this problem until now.

Then and Now

Even before ES6 classes took the world by storm -despite numerous people’s grievances- we had lots of options. JavaScript at a language level is prototypical, which solves the problem of behaviour re-use in itself. But framework owners had provided their solutions by creating their own object model that gives their users a simpler form of inheritance (Backbone, Ember, React…) that can be easily understood by developers who came from the back-end platforms. Most of these inheritance-based object models also came with mixin support to give developers a tool to let them use a more composition based approach.

When ES6 classes became the de facto way for javascript developers to achieve inheritance, a big part of JavaScript’s flexibility has been lost. This is one of the biggest reasons javascript decorators gain such wide adoption even when the feature was still in its infancy in stage-1. They provided that much needed flexibility.

Higher Order Functions

One other way to achieve behaviour re-use is higher order functions. They are a big part of the functional programming goodness. But what if the thing we want to decorate with a behaviour is not a function but a class? Can we talk about higher order classes? Is that even a thing? With javascript magic it kinda is. Technically, the one receiving and returning a class, is not another class but a function, but I think that’s ok since we are calling these things higher order components now.

I’d say solution that I’m going to show here is better than the current ones in use, because;

It’s achieved by lots of ES2017 sugar, so it’s tasty.

It works without including a dependency, so there is no fat.

And most importantly in a clean and readable manner that what we do is transparent to the people reading it, so it looks good.

It makes use of the fact that classes are expressions in javascript and how that fact plays so nicely with the way decorators work in javascript.

Higher Order Classes

This thing is going to make much more sense if we knew couple of facts about JavaScript.

First, how do classes work in javascript? We know that they are just syntactic sugar above functions. So in turn, classes behave very similar to functions. You can have a class with no name, you can’t access the class itself inside the definition, you can assign classes to a variable through an expression etc. In JavaScript,class declarations are are expressions.

And second, how do decorators work in JavaScript? Their behaviour kinda gets demystified when when you understand this about them; @ is an operator that feeds the thing that its decorating to the function returned from expression by its right side, and replaces it with definition returned from that said function.

We’re gonna try to make use of these facts in this approach. Here’s how.

Notice that constructors are invoked starting from the class that we are decorating and then upwards. Beauty here is that you can even share initialisation behaviour of your capabilities easily through this approach, through the decorator constructors.

Behavioural Difference with Mixins and Inheritance

There are some behavioural differences with mixins that we need to be aware of.

Because of the order of execution;

We can not not override new behaviours added by the decorators

We won’t be able to call this.fly in the bird’s constructor

Methods you declared in the Bird class can not override the ones in flying and tweeting decorators, because they do not exist at that point. Calling this.fly in another method of Bird class is OK because in the moment of their execution there will be a fly method but they will not be there at the moment of declaration.

This might look like a big limitation but there is a way to get around it. If we want that kind of composition, this is how we achieve it;

In this case, we were able to override tweet method because in the moment of Bat class’ declaration since it already exists under the extended Birdinessclass.

How Do We Type Check?

Another problem arises when we try to type check classes that returned from this decorator. We might want to know if a class has the capability to do something before executing relevant code.

What I prefer using in this case is a named export in the same module we defined the decorator, beside the default export. One that provides a class that we can use with instanceof operator. This class will be the most basic form of a class decorated with our function because it uses the native Object class as its first parameter.

const decorator = target => class Tweeting extends target {
...
}

exportdefault decorator;
exportconst ATweeting = decorator(Object);

While this provides a basic Tweeting class that we can use with instance of checks rather than a function, those instanceof checks will always return false. It’s because each time we decorate a class with this approach we are creating a new class and those classes will be different from the ATweetingclass that we exported. Luckily JavaScript lets us override the default instanceof behaviour by overriding has.InstanceOf symbol.

We created a new unique JavaScript symbol named typeTag that is private to the decorator module. We then defined a private property in the extended class with initial value of typeTag. Lastly in the in the method we checked if the constructor of the checked instance has this private value to see if this decorator has been used on its class.