JavaScript Abstract Classes

In ES6 Javascript added some new features related to object-oriented programming. Like class, extends, static etc. Those features are very helpful for programmers.

If you know a little bit about Oops then you know Oops run around some core concept. Those concepts are class, encapsulation, Inheritance, polymorphism and abstraction. In this article, we will target abstraction.

What is an Abstraction?

So basically an Abstraction is a general implementation rather than an internal implementation. For example, you have a bulb and you deal with it using the switch button. We only know, how to switch on and switch off the bulb. But we don’t know, how to switch buttons work internally.

In Java, we have an abstract class and interface concept. Both concepts help to achieve abstraction. Where abstract class help to achieve 0 to 100% abstraction contrariwise interface provides always 100% of abstraction.

Here we are focusing only on an abstract class, not on an interface. But in javascript, both will work in the same manner. The only difference will be abstract class can contain abstract and non-abstract methods but the interface only contains only abstract methods. Here we are taking some properties of abstract class and trying to achieve them in javascript.

Property of Abstract Class

Property 1

But in Javascript, Neither we have an abstract keyword nor an interface keyword. So the first property can’t be used in javascript.

Property 2

Due to the first property not working in javascript. So we can’t use the second property also.

Property 3

With the help of this property, we can restrict the creation of objects of an abstract class.
Now comes to the point. How we can achieve this in javascript? So here we have a UML diagram and our ultimate goal is to achieve this inside javascript language.

JavaScript Abstract Classes
JavaScript Abstract Classes

class Beverage {
    #name;

    setType(name) {
        this.#name = name;
    }

    display() {
        console.log("This beverage is a type of " + this.#name);
    }
}

var beverageType = new Beverage();
beverageType.setType("Coffee");
beverageType.display(); // This beverage is a type of Coffee

It’s a basic class structure in javascript. Now here you can see we have created beverageType object for the Beverage class. Now we need to restrict this creation of the Beverage class Object.


class Beverage {

    constructor() {

        if (this.constructor === Beverage) {
            throw new TypeError("Abstract class 'Beverage' cannot be instantiated directly.");
        }
    }
}

var beverageType = new Beverage(); 
// Abstract class 'Beverage' cannot be instantiated directly.

Property 4

Now using this code can restrict the instantiation of the Beverage class. So if we can’t create the instance of this class then how do we use this class? Now inheritance comes into the picture.


class Beverage {

    constructor() {

        if (this.constructor === Beverage) {
            throw new TypeError('Abstract class "Beverage" cannot be instantiated directly.');
        }
    }
}

class Coffee extends Beverage {
    #name;

    setType(name) {
        this.#name = name;
    }
    
    display() {
        console.log("This beverage is a type of " + this.#name);
    }
}

var beverageType = new Coffee();
beverageType.setType("Coffee");
beverageType.display(); // This beverage is a type of coffee.

Property 5

Now time to achieve the fourth property. Let’s take a complete example with an abstract class and abstract method.


class Beverage {

    constructor() {

        if (this.constructor === Beverage) {
            throw new TypeError("Abstract class 'Beverage' cannot be instantiated directly.");
        }
    }
    
    // Implementation required
    with(addon) {
        throw new Error("Abstract class 'Beverage' Method 'with' must be implemented.");
    }

    // Implementation optional
    warning() {
        console.log("All beverage are potable.");
    }
}

class Coffee extends Beverage {
    #name;
    #addon;

    setType(name) {
        this.#name = name;
    }

    with(addon) {
        this.#addon = addon;
    }
    
    display() {
        console.log("This beverage is a type of " + this.#name + " with " + this.#addon);
    }
}

var beverageType = new Coffee();
beverageType.setType("Coffee");
beverageType.with("Milk");
beverageType.display(); // This beverage is a type of Coffee with Milk
beverageType.warning(); // All beverage are potable.

Property 6

But in the previous example, we are not enforcing to override to an abstract method. We can achieve this through this snippet.


class Beverage {

    constructor() {

        if (this.constructor === Beverage) {
            throw new TypeError("Abstract class 'Beverage' cannot be instantiated directly.");
        }

        if (this.with === undefined) {
            throw new TypeError("Subclass must override an abstract method with()" );
        }
    }
}

class Coffee extends Beverage {
    constructor() {
        super();
    }
}

var beverageType = new Coffee();
// Subclass must override an abstract method with()

Now you are enforcing everyone to override with the method inside the subclass.

About the Author: Pankaj Bisht