How to create abstract classes in TypeScript?


Introduction to Abstraction

We wanted readers to get familiar with abstract classes and the requirements of abstract classes before implementing them. The abstraction means hiding. It is used to hide the lower-level code implementation from users and some developers. Furthermore, it is used to show only the required information about the method rather than showing the whole complex implementation of methods.

Create Abstract Classes

We can use the abstract keyword to define the abstract classes or methods. The abstract classes can contain the normal and abstract types of methods. In the abstract class, we need to implement the functional or normal method and only need to declare the abstract method.

We can inherit the abstract class using any other class, but we need to implement all abstract methods of the abstract class into the inherited class. If we don’t want to implement the abstract method into the inherited class, we need to make an inherited class to abstract using the abstract keyword.

Also, we can’t create the object of the abstract class, but we can create the object of the inherited class and use the abstract class methods. The limitation of the abstract class is that we can’t implement multiple inheritances using the multiple abstract classes.

Syntax

Users can follow the syntax below to create and inherit the abstract class to other classes.

abstract class sample {
   // define variables inside the abstract class,
   // declare the abstract methods or non-abstract method inside the abstract class
   abstract demo(string): void;
}
// extend sample class and implement all abstract methods of sample to demo class
class test extends sample {
   demo(name: string): void {
      // code for the demo method
   }
}

Steps

  • Step 1 − Define the abstract class named sample, which contains properties named propert1 and property2.

  • Step 2 − Also, add the constructor into the sample class to initialize the values of property1 and property2.

  • Step 3 − Declare the abstract method name demo() inside the abstract class. Also, define the save() method of the abstract class, a non-abstract method that prints the values of property1 and property2.

  • Step 4 − Define the test class, a non-abstract class that inherits the sample class. Also, add the property3 of the string type to the test class.

  • Step 5 − Add the constructor to the test class, which takes 3 parameters, and using the first 2 parameters, we call the constructor of the superclass, which is the sample class.

  • Step 6 − Implement the demo() method of the sample class inside the test class, which prints the value of the property3.

Example 1

In the example below, we have defined the abstract class containing the abstract methods. In the inherited test class, we have implemented the abstract methods of the sample class. Next, we created the object of the test class with 3 arguments and used that called the demo() and save() methods.

abstract class sample {
   // define variables inside the abstract class,
   property1: string;
   constructor(property1: string, property2: number) {
      this.property1 = property1;
   }
   // declare the abstract methods
   abstract demo(): void;
   
   // defining the non-abstract methods
   save(): void {
      console.log("The save method of the abstract class is executed.");
   }
}
// extend sample class and implement all abstract methods of sample to demo class
class test extends sample {
   property2: number;
   constructor(property1: string, property2: number) {
      super(property1);
      this.property2 = property2;
   }
   demo(): void {
      // code for the demo method
      console.log("The value of the property 3 is " + this.propert2);
   }
}
let test_obj = new test("TutorialsPont", 9999);
test_obj.demo();
test_obj.save();

We have hidden the implementation of the save() method from the inherited class test in the above example. We allow developers to implement the demo() method as they want but hide the other class information, such as property1, property2, and implementation of the save() method.

Now, users understand correctly the motive to use the abstract class and how we can use it to hide the information and can reveal only the required information.

On compiling, the above code will generate the following JavaScript code −

var __extends = (this && this.__extends) || (function () {
   var extendStatics = function (d, b) {
      extendStatics = Object.setPrototypeOf ||
      ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
      function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
      return extendStatics(d, b);
   };
   return function (d, b) {
      extendStatics(d, b);
      function __() { this.constructor = d; 
   }
   d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var sample = /** @class */ (function () {
   function sample(property1, property2) {
      this.property1 = property1;
   }
   // defining the non-abstract methods
   sample.prototype.save = function () {
      console.log("The save method of the abstract class is executed.");
   };
   return sample;
}());
// extend sample class and implement all abstract methods of sample to demo class
var test = /** @class */ (function (_super) {
   __extends(test, _super);
   function test(property1, property2) {
      var _this = _super.call(this, property1) || this;
      _this.property2 = property2;
      return _this;
   }
   test.prototype.demo = function () {
      // code for the demo method
      console.log("The value of the property 3 is " + this.propert2);
   };
   return test;
}(sample));
var test_obj = new test("TutorialsPont", 9999);
test_obj.demo();
test_obj.save();

Output

It will produce the following output −

The value of the property 3 is undefined
The save method of the abstract class is executed.

Example 2

In the example below, the class1 is the abstract class, which contains the declaration of the abstract method name method1. The class2 only contains the definition of method2(). It extended class1 but didn’t implement the abstract method named method1().

After that, we defined class3 and inherited it via class2. Also, we have defined the method1 of class inside class3. At last, we created the object of class3 and invoked the method1() and method2().

// define the abstract class1 containing the abstract method1
abstract class class1 {
   abstract method1(): void;
}
// Need to create class2 to abstract as we inherited class1 but doesn't defined abstract method1()
abstract class class2 extends class1 {
   method2(): void {
      console.log("Inside the method 2 of class2.");
   }
}
// defining the class3 inherited by the class2
class class3 extends class2 {
   // Implementation of the method1 of the abstract class1
   method1(): void {
      console.log(
         "Implemented the abstract method name method1 of class1 inside the class3"
      );
   }
}
// Crating the object of the class3
var object = new class3();

// Invoking the method1 of class1 which is declared in the abstract class1
object.method1();

// Invoking the method2 of class2
object.method2();

The above example shows us that if we inherit the abstract class by any class and don’t want to implement the abstract method into the inherited class, we need to make the inherited class abstract.

On compiling, above code will generate the following JavaScript code −

var __extends = (this && this.__extends) || (function () {
   var extendStatics = function (d, b) {
      extendStatics = Object.setPrototypeOf ||
         ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
         function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
         return extendStatics(d, b);
      };
      return function (d, b) {
         extendStatics(d, b);
         function __() { this.constructor = d; }
         d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
      };
})();
// define the abstract class1 containing the abstract method1
var class1 = /** @class */ (function () {
   function class1() {
   
   }
   return class1;
}());
// Need to create class2 to abstract as we inherited class1 but doesn't defined abstract method1()
var class2 = /** @class */ (function (_super) {
   __extends(class2, _super);
   function class2() {
      return _super !== null && _super.apply(this, arguments) || this;
   }
   class2.prototype.method2 = function () {
      console.log("Inside the method 2 of class2.");
   };
   return class2;
}(class1));
// defining the class3 inherited by the class2
var class3 = /** @class */ (function (_super) {
   __extends(class3, _super);
   function class3() {
   return _super !== null && _super.apply(this, arguments) || this;
   }
   // Implementation of the method1 of the abstract class1
   class3.prototype.method1 = function () {
      console.log("Implemented the abstract method name method1 of class1 inside the class3");
   };
   return class3;
}(class2));
// Crating the object of the class3
var object = new class3();

// Invoking the method1 of class1 which is declared in the abstract class1
object.method1();

// Invoking the method2 of class2
object.method2();

Output

It will will produce the following output −

Implemented the abstract method name method1 of class1 inside the class3
Inside the method 2 of class2.

Users learned to implement the abstract classes in this tutorial. Now, users can understand how we are hiding the other information of the class using the abstract class and abstract methods.

Also, users can use the interfaces for abstraction. All methods of the interface are abstract in TypeScript, and it doesn’t allow the non-abstract method. Also, we can use the interface for multiple inheritances.

Updated on: 16-Jan-2023

869 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements