Categories
TypeScript

Using TypeScript — Class Inheritance and Interfaces

Spread the love

TypeScript is a natural extension of JavaScript that’s used in many projects in place of JavaScript.

However, not everyone knows how it actually works.

In this article, we’ll look at how to work with classes and interfaces in TypeScript.

Class Inheritance

TypeScript builds on the standard clas inheritance features to make them more familiar to developers using other programming languages.

It keeps the extends keyword like JavaScript for creating subclasses.

For instance, we can write:

class Person {
  constructor(public name: string) {}
}

class Student extends Person {
  constructor(readonly id: number, public name: string) {
    super(name);
  }

  read() {
    //...
  }
}

We have the Student subclass that has its own parameters in the constructor.

We use the super keyword to call the parent constructor.

Also, we have a read method that’s exclusive to the Student class.

Type Inference for Subclasses

The TypeScript compiler may not be able to infer types from classes.

It’s easy to produce unexpected results by assuming that it has insights into the structure of the created objects.

For instance, if we have:

class Person {
  constructor(public name: string) {}
}

class Student extends Person {
  constructor(readonly id: number, public name: string) {
    super(name);
  }

  read() {
    //...
  }
}

const person = new Person("james");
const student = new Student(1, "james");

const people: (Person | Student)[] = [person, student];
people.forEach(p => {
  if (p instanceof Person) {
    console.log("Person");
  } else {
    console.log("Student");
  }
});

We’ll see that both objects are considered to be an instance of Person.

So we get 'Person' logged for both objects.

Abstract Class

Abstract classes are classes that can’t be instantiated directly and are used to describe common functionality that must be implemented by subclasses.

It forces subclasses to adhere to a specific structure but allows for class-specific implementations of specific methods.

To define one, we can use the abstract keyword.

For instance, we can write:

abstract class Person {
  constructor(public name: string) {}
  getName(): string {
    return this.name;
  }
  abstract getSpecificDetails(): string;
}

class Student extends Person {
  constructor(public id: string, public name: string) {
    super(name);
  }

  getSpecificDetails() {
    return `${this.id} - ${this.name}`;
  }
}

The code above has an abstract Person class with a getName method and a constructor.

Then we have the Student class that implements the Person class.

This is indicated with the extends keyword.

We implemented the getSpecificDetails and return a string.

Type Guarding an Abstract Class

Abstract classes are implemented as regular classes in JavaScript generated by the TypeScript compiler.

The TypeScript compiler prevents us from creating objects with abstract classes.

However, this isn’t carried over to the JavaScript code, which means that we may be able to create objects with classes that are declared abstract in our TypeScript code.

Using Interfaces

Interfaces are similar to object shape types.

A class can implement an interface, which means it must conform to the interface.

For instance, we can write:

interface Person {
  name: string;
  getName(): string;
}

class Employee implements Person {
  constructor(public name: string) {}
  getName() {
    return this.name;
  }
}

We have a Person interface that has the name and getName members.

Then since Employee implements the Person interface as indicated by the implements keyword, we’ve to include both members.

The interface keyword indicates that it’s an interface.

Merging Interface Declarations

We can merge multiple interfaces with the | to form a union or & to form an intersection.

It’s like unions or intersections with object shape types. Intersections mean all members of both interfaces must be included in our object.

Union means that any member of the interface can be included.

Implementing Multiple Interfaces

We can implement multiple interfaces in one class. Then we need to implement all the members of both interfaces.

For instance, we can write:

interface Person {
  name: string;
  getName(): string;
}

interface Owner {
  ownedItems(): string[];
}

class Employee implements Person, Owner {
  constructor(public name: string) {}
  getName() {
    return this.name;
  }

  ownedItems() {
    return ["food"];
  }
}

We have an Employee class that implements both Person and Owner interfaces.

It has all the members from both.

Conclusion

We can implement inheritance with TypeScript. It improves the class syntax’s inheritance syntax.

TypeScript has abstract classes and interfaces to enforce the implementation of classes.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *