Back to Course

Learn TypeScript

0% Complete
0/0 Steps
Lesson 10 of 25
In Progress

Extending Interfaces and Classes

Welcome to the “Extending Interfaces and Classes in TypeScript” section of our course “Learn TypeScript”! In this article, we will cover how to extend interfaces and classes in TypeScript, and how you can use these features to write more structured and reusable code. By the end of this article, you should have a good understanding of these concepts and be able to use them effectively in your TypeScript programs.

Introduction to Extending Interfaces

Interfaces in TypeScript can be extended using the “extends” keyword. This allows you to define a new interface that includes all the members of the base interface, and can also add additional members of its own. For example:

interface Point {
  x: number;
  y: number;
}

interface ThreeDimensionalPoint extends Point {
  z: number;
}

In this example, we have defined an interface called “ThreeDimensionalPoint” that extends the “Point” interface. The “ThreeDimensionalPoint” interface includes all the members of the “Point” interface, as well as an additional member called “z”.

A class or object that implements the “ThreeDimensionalPoint” interface must implement all the members of both the “ThreeDimensionalPoint” and “Point” interfaces. For example:

class CartesianPoint implements ThreeDimensionalPoint {
  x: number;
  y: number;
  z: number;

  constructor(x: number, y: number, z: number) {
    this.x = x;
    this.y = y;
    this.z = z;
  }
}

In this example, we have defined a class called “CartesianPoint” that implements the “ThreeDimensionalPoint” interface. The class defines all the properties specified in the “ThreeDimensionalPoint” and “Point” interfaces.

Introduction to Extending Classes

Classes in TypeScript can also be extended using the “extends” keyword. This allows you to create a subclass that inherits the properties and methods of the base class, and can also define additional properties and methods of its own. For example:

class Shape {
  area(): number {
    return 0;
  }
}

class Rectangle extends Shape {
  width: number;
  height: number;

  constructor(width: number, height: number) {
    super();
    this.width = width;
    this.height = height;
  }

  area() {
    return this.width * this.height;
  }
}

In this example, we have defined a class called “Rectangle” that extends the “Shape” class. The “Rectangle” class defines an additional property called “width” and an additional method called “area” that calculates the area of the rectangle.

The “Rectangle” class also calls the “super()” method in the constructor, which calls the constructor of the base class. This is important because it allows the base class to initialize its own properties and set up any necessary state before the subclass starts working with those properties.

It’s worth noting that the “extends” keyword can also be used to extend interfaces. For example, you could define an interface called “Shape” and then define a class called “Rectangle” that extends that interface.

Overriding Methods in Subclasses

When you create a subclass in TypeScript, you can override the methods of the base class by defining a method with the same name in the subclass. The subclass’s implementation of the method will be used instead of the base class’s implementation. For example:

class Shape {
  area(): number {
    return 0;
  }
}

class Rectangle extends Shape {
  width: number;
  height: number;

  constructor(width: number, height: number) {
    super();
    this.width = width;
    this.height = height;
  }

  area() {
    return this.width * this.height;
  }
}

let rectangle = new Rectangle(10, 20);
console.log(rectangle.area()); // Outputs: 200

In this example, we have defined a “Rectangle” class that extends the “Shape” class and overrides the “area” method. When we call the “area” method on an instance of the “Rectangle” class, it uses the subclass’s implementation of the method, which calculates the area of the rectangle using the width and height properties.

It’s important to note that when you override a method in a subclass, you should ensure that the subclass’s implementation of the method is compatible with the base class’s implementation. For example, if the base class’s method returns a number, the subclass’s method should also return a number. This helps to maintain the integrity of your code and avoid runtime errors.

Abstract Classes

In TypeScript, you can define an abstract class using the “abstract” keyword. An abstract class is a class that cannot be instantiated directly, but can be subclassed. Abstract classes are useful when you want to define a base class that provides common functionality, but requires subclasses to provide specific implementations for certain methods.

For example, you might define an abstract “Shape” class that has an “area” method, but leave the implementation of the “area” method up to the subclasses. This allows you to define the general structure of a shape, while still allowing subclasses to define their own specific behavior.

To define an abstract method in an abstract class, use the “abstract” keyword followed by the name of the method and its signature. For example:

abstract class Shape {
  abstract area(): number;
}

Subclasses of an abstract class must implement all the abstract methods of the base class. If a subclass does not implement an abstract method, it must also be marked as “abstract”. For example:

abstract class Shape {
  abstract area(): number;
}

class Rectangle extends Shape {
  width: number;
  height: number;

  constructor(width: number, height: number) {
    super();
    this.width = width;
    this.height = height;
  }

  area() {
    return this.width * this.height;
  }
}

abstract class ThreeDimensionalShape extends Shape {
  abstract volume(): number;
}

In this example, we have defined an abstract “ThreeDimensionalShape” class that extends the “Shape” class and adds an abstract “volume” method. Since the “ThreeDimensionalShape” class does not provide an implementation for the “volume” method, it must also be marked as “abstract”.

It’s worth noting that abstract classes can also have concrete methods, which are methods that have a complete implementation. Concrete methods can be called directly on an instance of an abstract class, while abstract methods must be implemented by subclasses.

Conclusion

In conclusion, this article has covered how to extend interfaces and classes in TypeScript, and how you can use these features to write more structured and reusable code. You should now have a good understanding of these concepts and be able to use them effectively in your TypeScript programs. In future sections of our course, we will cover more advanced topics, such as generics and decorators, and how to use them to write more powerful TypeScript code.

Exercises

To review these concepts, we will go through a series of exercises designed to test your understanding and apply what you have learned.

Can an abstract class have an abstract constructor in TypeScript?

No, an abstract class cannot have an abstract constructor in TypeScript. The purpose of an abstract constructor is to enforce that a class must have a specific constructor, but abstract classes cannot be instantiated directly. Therefore, it does not make sense to define an abstract constructor for an abstract class.

Can an abstract class have concrete properties in TypeScript?

Yes, an abstract class can have concrete properties in TypeScript. Concrete properties are properties that have a fixed value, and can be accessed directly on an instance of the abstract class.

Can a class that extends an abstract class override an abstract method with a concrete method in TypeScript?

Yes, a class that extends an abstract class can override an abstract method with a concrete method in TypeScript. When you override a method in a subclass, you can provide a complete implementation for the method using the “concrete” syntax. For example:

abstract class Shape {
  abstract area(): number;
}

class Rectangle extends Shape {
  width: number;
  height: number;

  constructor(width: number, height: number) {
    super();
    this.width = width;
    this.height = height;
  }

  area() {
    return this.width * this.height;
  }
}

In this example, we have defined an abstract “Shape” class with an abstract “area” method, and a “Rectangle” class that extends the “Shape” class and overrides the “area” method with a concrete implementation.

Can an abstract method have a default implementation in TypeScript?

No, an abstract method cannot have a default implementation in TypeScript. The purpose of an abstract method is to define a contract that requires subclasses to provide their own implementation for the method. If an abstract method had a default implementation, it would defeat the purpose of the abstract method and make it less flexible.

Can an abstract class have a concrete constructor in TypeScript?

Yes, an abstract class can have a concrete constructor in TypeScript. A concrete constructor is a constructor that has a complete implementation, and can be called directly on an instance of the abstract class. For example:

abstract class Shape {
  abstract area(): number;

  constructor(public name: string) { }
}

class Rectangle extends Shape {
  width: number;
  height: number;

  constructor(name: string, width: number, height: number) {
    super(name);
    this.width = width;
    this.height = height;
  }

  area() {
    return this.width * this.height;
  }
}

In this example, we have defined an abstract “Shape” class with an abstract “area” method and a concrete constructor that takes a “name” parameter. The “Rectangle” class extends the “Shape” class and provides its own implementation of the “area” method, and also calls the “super()” method in the constructor to pass the “name” parameter to the base class.