Abstract Classes in Typescript?

April 27, 2020

In Typescript the classes can inherit from another class for share methods and properties between classes also Typescript support abstract class, let me show why and when to use it.

For example, we have a base class Subscription and create the new subclass FrenchSubscription with his own sign method implementation.

class Subscription {
	url: string = "";
	constructor(public name: string) {
		this.name = name;
	}
	sign(): string {
		return "Signup!!";
	}
}

class FrenchSubscription extends  Subscription {
	url: string = "http://www.typescript.fr";
	sign(): string {
		return `Bonjour ${this.name} please go to ${this.url}`;
	}
}

We create a new SubscriptionManager class to process a list of subscriptions and use the sign method to show a message.

The SubscriptionManager will support future subscriptions child class, like EnglishSubscription class.

The SubscriptionManager has a process method to iterate over every subscription and call the sign method to show the message.

class SubscriptionManager {
	static subscriptions: Array<Subscription> = [];
	static process() {
		this.subscriptions.forEach((p) => {
			let message =  p.sign();
			console.log(message)
		})
	}
}

The SubscriptionManager is ready to works, then add a Subscription instance to subscriptions and process it.

let france = new FrenchSubscription('Paris');
SubscriptionManager.subscriptions.push(france);

SubscriptionManager.process();
[nodemon] starting `node Student.js`
Bonjour Paris please go to http://www.typescript.fr

Nodemon runs the app, and perfect!! everything works! go to live!! my code works but has some weaknesses.

A new developer comes to the company and needs to create a new class for English, it will inherit from the Subscription base class. He creates EnglishSubscription and the sign method because everything looks fine IDE, and the compiler won’t complain about it. commit his changes.

class EnglishSubscription extends Subscription {
	url: string = "http://www.typescript.us";
	sign() {
		return false;
	}
}

The service start to use the EnglishSubscription class, and it is a Subscription object will contains the sign method.

let france = new FrenchSubscription('Paris');
let ny = new EnglishSubscription('New York');

SubscriptionManager.subscriptions.push(france);
SubscriptionManager.subscriptions.push(ny)

SubscriptionManager.process();

CRASHH!! the result is not as we expect!!!

Bonjour Paris please go to http://www.typescript.fr
false

We had a bug and error, the method exists but doesn’t fit with the initial contract and the developer doesn’t know how what is the contract with the SubscriptionManager.

Some times we need to be sure the developer fits with the goal of the base class and be sure every child class based on it implements all members with field, methods, and signature.

Then, it is time for use the abstract class, using the abstract keyword before the method and class name them our normal class becomes to be an abstract class and every child class must implement the method with the expected signature.

Tip: The abstract methods don’t have an implementation.

abstract class Subscription {
	url: string = "";
	constructor(public name: string) {
		this.name = name;
	}
	abstract sign(): string { }
}

The child classes from Subscription must implement the sign method with the signature, if not the IDE and compiler will raise an error.

 Property 'sign' in type 'EnglishSubscription' is not assignable to the same property in base type 'Subscription'. 
  Type '() => boolean' is not assignable to type '() => string'.     Type 'boolean' is not assignable to type 'string'.

Using abstract the developers understand what is the goal of the base class Subscription, and his methods also we are sure for each child class implements the methods for the proposal with an expected result but different behavior and our code is safe and flexible.

Hopefully, that will give you a bit of help with Abstract classes in Typescript. If you enjoyed this post, share it.