Abstraction in TypeScript

This post is 4 years old. (Or older!) Code samples may not work, screenshots may be missing and links could be broken. Although some of the content may be relevant please take it with a pinch of salt.

Abstraction is an Object Oriented programming concept whereby a class can specify how an inherited class should implement the class itself, including any abstract methods specified.

The concept behind abstraction is that we are hiding the complexity of the implementation from inherited classes and we allow them to implement the functionality themselves.

In TypeScript abstraction can be achieved by using the abstract keyword - which can be applied to both classes and methods specified in classes.

If you're new to TypeScript classes please read this introductory article.

Let's go ahead and create an abstract class with an abstract method as well:

abstract class Warrior {
readonly name: string;
public weapon: string;
constructor(name: string) {
this.name = name;
}

abstract arm(weapon: string): void;
}

If we now attempt to instansiate this class we'll get an error:

const john = new Warrior('John, the Nomad'); // Cannot create an instance of the abstract class 'Warrior'.

In order to implement our abstract class we need to create a subclass and extend it using the extends keyword - and we also need to make sure that we implement the method arm() in our subclass, otherwise we'll receive an error: Non-abstract class 'SuperWarrior' does not implement inherited abstract member 'arm' from class 'Warrior'.`

class SuperWarrior extends Warrior {
constructor(name: string) {
super(name);
}
arm(weapon: string): void {
console.log(`${this.name} is a super warrior fighting with ${weapon}`);
}
}

Now that we have the subclass we can create our warrior:

const warrior = new SuperWarrior('John, the Nomad');
warrior.arm('lance'); // John, the Nomad is a super warrior fighting with lance

Of course since we are writing TypeScript we can also utilise Interfaces and implement them for classes, here's the final code:

interface IWarrior {
name: string;
weapon: string;
arm(weapon: string): void;
}
abstract class Warrior implements IWarrior {
readonly name: string;
public weapon: string;
constructor(name: string) {
this.name = name;
}

abstract arm(weapon: string): void;
}

class SuperWarrior extends Warrior {
constructor(name: string) {
super(name);
}
arm(weapon): void {
console.log(`${this.name} is a super warrior fighting with ${weapon}`);
}
}

const warrior: IWarrior = new SuperWarrior('John, the Nomad');
warrior.arm('lance');