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
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
// 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
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
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
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("[email protected]")
];
// 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
Other extensionsRecommendation:When designing class hierarchies, prioritize using abstract classes to share code and define specifications.