What is abstract class in TypeScript?
An abstract class in TypeScript is a special type of class that cannot be instantiated directly. It serves as a blueprint for other classes, defining common properties and methods that derived classes must or may implement. Abstract classes are primarily used to define a common interface and provide a base implementation for a group of related objects.
Definition
An abstract class is declared using the abstract keyword. It can contain both abstract members (methods and properties) and non-abstract (concrete) members. Abstract members are declared without an implementation; they must be implemented by any non-abstract derived class. Non-abstract members provide a default implementation that derived classes can inherit or override.
Key Characteristics
- Cannot be instantiated: You cannot create an object directly from an abstract class using the
newkeyword. - Requires subclasses: Abstract classes are meant to be extended by other concrete classes.
- Can have abstract members: They can declare methods and properties as abstract, meaning they don't have an implementation in the abstract class itself.
- Abstract members must be implemented: Any non-abstract class that extends an abstract class must provide an implementation for all inherited abstract methods and properties.
- Can have concrete members: They can also have regular (non-abstract) methods and properties with full implementations, which derived classes can inherit or override.
- Acts as a base class: Provides a common contract and potentially partial implementation for derived classes.
When to Use Abstract Classes
- Defining a common interface with partial implementation: When you want to define a common structure and some shared behavior, but also require subclasses to provide their specific implementations for certain operations.
- Base classes for a family of related objects: To ensure that all derived classes adhere to a specific contract and share some foundational logic.
- Encouraging specific behavior: When you want to force subclasses to implement certain methods, ensuring a particular functionality exists across all derived types.
- Refactoring common code: To move common methods and properties from multiple concrete classes into a single base abstract class.
Example
Consider an example where we want to define different types of shapes, each with a method to calculate its area and a common method to display information.
abstract class Shape {
name: string;
constructor(name: string) {
this.name = name;
}
// An abstract method that must be implemented by derived classes
abstract getArea(): number;
// A concrete method with an implementation
displayInfo(): void {
console.log(`This is a ${this.name} with an area of ${this.getArea()} square units.`);
}
}
class Circle extends Shape {
radius: number;
constructor(name: string, radius: number) {
super(name);
this.radius = radius;
}
// Implementation of the abstract method
getArea(): number {
return Math.PI * this.radius * this.radius;
}
}
class Rectangle extends Shape {
width: number;
height: number;
constructor(name: string, width: number, height: number) {
super(name);
this.width = width;
this.height = height;
}
// Implementation of the abstract method
getArea(): number {
return this.width * this.height;
}
}
// We cannot instantiate an abstract class directly
// const myShape = new Shape("Generic Shape"); // Error: Cannot create an instance of an abstract class.
const circle = new Circle("Circle", 10);
console.log(circle.getArea()); // Output: 314.159...
circle.displayInfo(); // Output: This is a Circle with an area of 314.159... square units.
const rectangle = new Rectangle("Rectangle", 5, 8);
console.log(rectangle.getArea()); // Output: 40
rectangle.displayInfo(); // Output: This is a Rectangle with an area of 40 square units.
Abstract Classes vs. Interfaces
While both abstract classes and interfaces define contracts, they have key differences:
- Implementation: Interfaces only declare members (methods, properties) without implementation. Abstract classes can declare members and also provide concrete implementations.
- Instantiation: Neither can be instantiated directly. However, an abstract class requires you to extend it, while an interface requires you to implement it.
- Inheritance: A class can implement multiple interfaces but can only extend one abstract class (or any other class).
- Constructors: Abstract classes can have constructors; interfaces cannot.
- Access Modifiers: Abstract classes can have
public,protected, andprivatemembers. Interfaces only implicitly havepublicmembers.
In essence, use an interface when you want to define a contract for what a class *can do*. Use an abstract class when you want to define a common base with some shared behavior and a contract for what a class *is* and *must do*.