TypeScript Abstract Classes

Abstract classes are an important concept in TypeScript object-oriented programming.

It is a class that cannot be instantiated directly and can only serve as a base class for other classes to inherit.

Abstract classes are mainly used to define common properties and methods for subclasses, providing a unified structure and behavior template for subclasses.


SVG Diagram: Abstract Class Inheritance Hierarchy Background Title Abstract Class Inheritance Hierarchy Structure Abstract class Animal abstract class Animal Cannot be instantiated directly Arrows to subclasses Subclass Dog class Dog extends Animal Subclass Cat class Cat extends Animal Abstract method description Abstract class features Feature list abstract method: subclasses must implement it Concrete method: subclasses can use it directly Can be used as a type: accepts subclass instances Arrow marker

Why Abstract Classes Are Needed

In object-oriented programming, we often need to define base classes.

These base classes describe common properties and methods for subclasses, but the implementation details of some methods need to be decided by the subclasses themselves.

Abstract classes are used to solve this problem: they allow us to define method signatures (declarations) while leaving the concrete implementation to subclasses.

Concept explanation:An abstract class is a type between a normal class and an interface. It has both interface characteristics (defining method signatures) and class characteristics (can have concrete method implementations and constructors).


Abstract Class Basics

Useabstractthe `abstract` keyword to declare abstract classes and abstract methods.

Abstract classes can contain abstract methods and concrete methods (methods with existing implementations).

Example

// Use the abstract keyword to declare an abstract class
// An abstract class cannot be instantiated directly; it can only serve as a base class
abstract class Animal {
    // Animal name property
    name: string;

    // Constructor
    constructor(name: string) {
        this.name = name;
    }

    // Abstract method: modified with abstract, no method body
    // Subclasses must implement this method
    abstract speak(): void;

    // Concrete method: a method with a specific implementation
    // Subclasses can directly inherit and use it without overriding
    move(): void {
        console.log(this.name + " is moving");
    }
}

// Attempting to instantiate an abstract class will cause an error
// var animal = new Animal("Animal"); // Error: cannot instantiate an abstract class

// Define the Dog class that inherits from Animal
class Dog extends Animal {
    // The subclass must implement the abstract method speak() of the parent class
    speak(): void {
        console.log(this.name + " Woof woof!");
    }
}

// Create a Dog instance
var dog = new Dog("Wangcai");
dog.speak();  // Call the method implemented by the subclass
dog.move();   // Inherit the concrete method from the parent class

Output:

Wangcai, woof woof woof!
Wangcai is moving.

Explanation:Abstract methods have no method body (only a method signature), and subclasses must implement them. Concrete methods have specific implementations, and subclasses can directly inherit and use them.


Abstract Methods

Abstract methods are methods declared but not implemented in an abstract class.

After a subclass inherits an abstract class, it must implement all abstract methods; otherwise, an error will occur.

Example

// Define abstract class Shape
abstract class Shape {
    // Abstract method: only declaration, no implementation
    // Subclasses must implement these two methods
    abstract area(): number;
    abstract perimeter(): number;

    // Concrete method: can use abstract methods
    // This method calls an abstract method; subclasses can use it normally after inheriting
    describe(): void {
        console.log("Area: " + this.area().toFixed(2));
    }
}

// Define a rectangle class that inherits Shape
class Rectangle extends Shape {
    // Rectangle width
    width: number;
    // Rectangle height
    height: number;

    // Constructor
    constructor(width: number, height: number) {
        super();  // Call the parent class constructor
        this.width = width;
        this.height = height;
    }

    // Implement abstract method: calculate area
    area(): number {
        return this.width * this.height;
    }

    // Implement abstract method: calculate perimeter
    perimeter(): number {
        return 2 * (this.width + this.height);
    }
}

// Create a rectangle instance
var rect = new Rectangle(4, 5);

// Call the inherited describe method, which internally calls the area method of the subclass
rect.describe();
console.log("Perimeter: " + rect.perimeter());

Output:

面积: 20.00
周长: 18

Tip:Concrete methods can call abstract methods. This is because when a concrete method is called, the subclass has already implemented the abstract method, so it can execute normally.


Abstract Class as a Type

An abstract class can be used as a parameter type.

This means a function can accept instances of any subclass of the abstract class, achieving polymorphism.

Example

// Define abstract class Animal
abstract class Animal {
    // Abstract method: all animals make sounds
    abstract speak(): void;
}

// Define Cat class that inherits Animal
class Cat extends Animal {
    // Implement abstract method
    speak(): void {
        console.log("Meow meow meow!");
    }
}

// Define Dog class that inherits Animal
class Dog extends Animal {
    // Implement abstract method
    speak(): void {
        console.log("Woof woof woof!");
    }
}

// Define the function parameter type as abstract class Animal
// This function can accept any subclass instance of Animal
function makeSpeak(animal: Animal): void {
    animal.speak();
}

// Passing different subclass instances results in different behaviors (polymorphism)
makeSpeak(new Cat());
makeSpeak(new Dog());

// Abstract class type array: can store instances of different subclasses
var animals: Animal[] = [new Cat(), new Dog()];

Output:

Meow meow meow!
Woof woof woof!

Polymorphism:When an abstract class is used as a type, the actual executed method implementation is the subclass's. This is the polymorphism feature of object-oriented programming.


Differences Between Abstract Classes and Interfaces

Both abstract classes and interfaces are used to define type specifications, but there are some key differences.

Understanding their differences helps make the right choice in actual development.

Feature Abstract Class Interface
Instantiation Cannot be instantiated directly Cannot be instantiated directly
Method implementation Can have concrete implementations Can only have declarations (TypeScript 3.6+ can have default implementations)
Member modifiers Can add public, protected, private Can only have readonly
Inheritance Single inheritance (can only extend one class) Multiple implementation (can implement multiple interfaces)
Constructor Can have constructor Cannot have constructor

Selection suggestions:Use abstract classes when shared code is needed; use interfaces when specifications/contracts need to be defined; use interfaces when multiple inheritance is needed.


Complete Example: Payment System

Below is an example of a payment system implemented using an abstract class.

This example demonstrates the application of abstract classes in real projects.

Example

// Define abstract payment class
abstract class Payment {
    // Abstract method: process payment, subclasses must implement it
    abstract process(amount: number): boolean;

    // Concrete method: verify payment information
    // All subclasses can use this method
    validate(): void {
        console.log("Verify payment information");
    }
}

// Credit card payment class
class CreditCardPayment extends Payment {
    // Credit card number
    cardNumber: string;

    // Constructor
    constructor(cardNumber: string) {
        super();  // Call the parent class constructor
        this.cardNumber = cardNumber;
    }

    // Implement abstract method: process credit card payment
    process(amount: number): boolean {
        console.log("Processing credit card payment: " + amount);
        return true;
    }
}

// PayPal payment class
class PayPalPayment extends Payment {
    // PayPal email
    email: string;

    // Constructor
    constructor(email: string) {
        super();
        this.email = email;
    }

    // Implement abstract method: process PayPal payment
    process(amount: number): boolean {
        console.log("Processing PayPal payment: " + amount);
        return true;
    }
}

// Use polymorphism to handle different payment methods
var payments: Payment[] = [
    new CreditCardPayment("1234"),
    new PayPalPayment("test@example.com")
];

// Iterate over and process each payment method
for (var _i = 0, payments_1 = payments; _i < payments_1.length; _i++) {
    var payment = payments_1[_i];
    // Call the inherited verification method
    payment.validate();
    // Call the subclass's processing method
    payment.process(100);
}

Output:

验证支付信息
处理信用卡支付: 100
验证支付信息
处理 PayPal 支付: 100

Application scenarios:Abstract classes are often used in framework design and business logic layering. For example, payment processing, user authentication, data persistence, and other scenarios.


Notes

  • Cannot be instantiated:Abstract classes cannot be directly instantiated with new; they must be inherited by subclasses
  • Abstract methods must be implemented:An error occurs if a subclass does not implement all abstract methods
  • Can have constructors:Abstract classes can define constructors, and subclasses need to call super()
  • Single inheritance:A class can only inherit one abstract class (single inheritance)
  • Access modifiers:Abstract methods can use public and protected modifiers

Best practices:Use abstract classes when multiple classes need to share code and logic. Use interfaces when only specifications and contracts need to be defined.


Summary

Abstract classes are an important part of TypeScript object-oriented programming.

  • The abstract keyword:Used to declare abstract classes and methods
  • Cannot be instantiated:Can only be used through inheritance by subclasses
  • Abstract methods:Methods that subclasses must implement
  • Concrete methods:Implemented methods that subclasses can directly inherit and use
  • Template method pattern:Abstract classes define the skeleton, subclasses provide concrete implementations

Recommendation:When designing class hierarchies, prioritize using abstract classes to share code and define specifications.

Other extensions