Abstract Classes in JavaScript
In Javascript, the concept of an abstract class is not natively supported as it is in other languages such as Java, TypeScript, and Python. However, we can write custom code to mimic the behavior of an abstract class in Javascript. In this article, I will explain what an abstract class is, why we need an abstract class, and how it can be implemented in JavaScript. Given that TypeScript is a programming language that is built on JavaScript, I’ll be using it to explain the concept of abstract classes for easier understanding.
What is an abstract class?
An abstract class is a class that cannot be instantiated.
This means that you cannot use an abstract class to directly create an object. if we think of a class as a blueprint for constructing a house for example, then an abstract class is a type of blueprint that we cannot use to directly build a house. Instead, an abstract class is a class that another class can inherit from. It acts as a base class for other classes. Using our blueprint analogy, it is a blueprint that another blueprint can use as its base.
In the example below, the class Shape is an abstract class, because it is defined using the abstract keyword. If we try to create an instance of the class Shape, we will get an error. The Circle class is a class that inherits from the abstract Shape class. As such it has access to its properties and methods just like a normal class would.
abstract class Shape {
name: string;
constructor(name: string) {
this.name = name;
}
}
class Circle extends Shape {
radius: number;
constructor(name: string, radius:number){
super(name);
this.radius = radius;
}
}
const myShape = new Shape('My shape'); // This will throw an Error
const shortCircle = new Circle("Short Circle", 0.5); // This will work fine.
One of the main features of an abstract class is that an abstract class can have an abstract method.
An abstract method is a method declared within an abstract class, but without any implementation in the abstract class.
If an abstract method is created within an abstract class, every class that inherits from the abstract class i.e. a subclass, must have an implementation of the abstract method. The implementation within the subclasses can be different (which is actually the purpose of the abstract method), but they all must define what the method will do.
NB: An abstract class can also have normal methods just like normal classes do
Normal methods in an abstract class are not enforced within subclasses and are available for subclasses to use or extend. In the example below, the getArea() method is an abstract method defined within the abstract Shape class. The Circle and Rectangle classes, both inherit from the Shape class, but only class Circle has an implementation for getArea(). Thus, when we try to create a new instance from Rectangle, we get an error saying the getArea() method needs to be implemented.
abstract class Shape {
name: string;
constructor(name: string) {
this.name = name;
}
abstract getArea(): number;
}
class Circle extends Shape {
radius: number;
constructor(name: string, radius:number){
super(name);
this.radius = radius;
}
getArea(): number {
return Math.PI * Math.pow(this.radius, 2);
}
}
class Rectangle extends Shape {
length: number;
width: number;
constructor(name:string, length:number, width:number){
super(name);
this.length = length;
this.width = width;
}
}
const smallRectangle = new Rectangle("Small Rectangle", 3, 5) // This will throw an error.
Why do we need an abstract class?
Abstract classes are useful for structuring and enforcing behaviors within our code. With an abstract class, developers can establish and enforce how each sub-class should be implemented. This helps with documentation and communicating the intended structure of our code. Abstract classes also act as a safeguard by preventing direct instantiation of a base class which could lead to unwanted bugs in a codebase. In addition, the risk of oversight of an implementation of a method within a sub-class is eliminated. This can help prevent bugs in our programs and ensure that our code works as intended.
Abstract classes in Javascript
Creating an abstract class in Javascript involves creativity. Here is a Javascript implementation of the code we wrote earlier:
class Shape {
constructor(name) {
if(this.constructor == Shape) {
throw new Error("Class is of abstract type and can't be instantiated");
};
if(this.getArea == undefined) {
throw new Error("getArea method must be implemented");
};
this.name = name;
}
}
class Rectangle extends Shape {
constructor(name, length, width){
super(name);
this.length = length;
this.width = width;
}
}
const myShape = new Shape('My shape'); // This will throw an Error
const smallRectangle = new Rectangle("Small Rectangle", 3, 5) // This will throw an error.
In the code above, we check if the new object created was created by the constructor function of the Shape class. If so, then the Shape class is being directly instantiated and we throw an error. We also ensure that the abstract method getArea() is being implemented in all subclasses of the shape class. If a subclass doesn’t have an implementation of getArea(), we throw an error.
By writing our code this way, we mimic the behavior of abstract classes making them applicable in JavaScript.
Final Thoughts.
Abstract classes are great in helping to structure and organize your code. If you are trying to create abstract classes in JS, I’ll recommend using TypeScript instead as it’s a great way to enforce other best practices, provide type safety, and natively support the abstract class concept.
However, If you really need to use JavaScript, now you know how to do it :)
Thanks for reading!