Overview
In TypeScript, abstract classes and interfaces are fundamental concepts that enable developers to design flexible and scalable applications while adhering to the principles of object-oriented programming (OOP). Abstract classes provide a base framework for other classes to extend, enforcing a certain structure while allowing for shared functionality. Interfaces define a contract for what a class can do, without specifying how it does it. Choosing between an abstract class and an interface depends on the specific requirements of your design.
Key Concepts
- Abstract Classes: Used to define a base class with common implementation that other classes can inherit, but cannot be instantiated on their own.
- Interfaces: Used to define a contract for classes without implementing any behavior themselves, allowing for a form of multiple inheritance by letting a class implement multiple interfaces.
- Encapsulation and Polymorphism: Both abstract classes and interfaces promote encapsulation and polymorphism, key principles of OOP, by ensuring that classes adhere to a certain structure and behavior while allowing for flexibility in implementation.
Common Interview Questions
Basic Level
- What is an abstract class, and when would you use it?
- Can a TypeScript interface contain implementation details?
Intermediate Level
- How can interfaces be used to implement multiple inheritance in TypeScript?
Advanced Level
- When designing a system, how do you decide whether to use an abstract class or an interface?
Detailed Answers
1. What is an abstract class, and when would you use it?
Answer: In TypeScript, an abstract class is a class that cannot be instantiated on its own and is designed to be subclassed. It can contain both abstract methods (without an implementation) and concrete methods (with an implementation). You would use an abstract class when you want to provide a common base class for other classes to extend, and when there is shared behavior among the subclasses that can be implemented within the abstract class itself.
Key Points:
- Abstract classes cannot be instantiated directly.
- They can contain a mix of abstract and concrete methods.
- Use abstract classes when there's shared behavior that can be implemented within the abstract class.
Example:
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log("Roaming the earth...");
}
}
class Dog extends Animal {
makeSound(): void {
console.log("Bark");
}
}
// const myAnimal = new Animal(); // Error: Cannot create an instance of an abstract class.
const myDog = new Dog();
myDog.makeSound(); // Outputs: Bark
myDog.move(); // Outputs: Roaming the earth...
2. Can a TypeScript interface contain implementation details?
Answer: No, a TypeScript interface cannot contain implementation details. Interfaces in TypeScript are purely a way to define the shape that an object should have. They are contracts for a class to follow, specifying what methods or properties a class must implement, without dictating how those methods should be implemented.
Key Points:
- Interfaces are contracts for class structure and behavior.
- They do not contain any implementation details.
- Interfaces allow for a form of multiple inheritance by enabling a class to implement multiple interfaces.
Example:
interface Movable {
move(): void;
}
class Car implements Movable {
move(): void {
console.log("Car is moving");
}
}
const myCar = new Car();
myCar.move(); // Outputs: Car is moving
3. How can interfaces be used to implement multiple inheritance in TypeScript?
Answer: TypeScript doesn't support multiple inheritance directly due to the complexity and ambiguity it can introduce. However, interfaces can be used to achieve a similar effect. A class in TypeScript can implement multiple interfaces, allowing it to inherit properties and methods from multiple sources, thus simulating multiple inheritance.
Key Points:
- Interfaces provide a way to implement multiple inheritance.
- A class can implement multiple interfaces.
- This allows a class to have diverse functionality from different interfaces.
Example:
interface Drivable {
drive(): void;
}
interface Flyable {
fly(): void;
}
class FutureCar implements Drivable, Flyable {
drive(): void {
console.log("Driving on the road");
}
fly(): void {
console.log("Flying in the sky");
}
}
const myFutureCar = new FutureCar();
myFutureCar.drive(); // Outputs: Driving on the road
myFutureCar.fly(); // Outputs: Flying in the sky
4. When designing a system, how do you decide whether to use an abstract class or an interface?
Answer: The choice between an abstract class and an interface depends on the design requirements of your system. Use an abstract class when you want to provide a common implementation for subclasses while still requiring them to implement specific methods. Use an interface when you want to define a contract for classes to follow without providing any implementation details. If you need to define functionality that multiple classes should use, but there is no logical hierarchical relationship, an interface is often the best choice. If there's a clear hierarchical relationship and shared implementation logic, an abstract class is more appropriate.
Key Points:
- Use abstract classes for a clear hierarchical relationship and shared implementation.
- Use interfaces to define a contract for behavior without implementation.
- Consider the relationship between the entities and whether there is shared implementation logic.
Example:
// Use an abstract class for a shared implementation and hierarchical relationship
abstract class Vehicle {
startEngine(): void {
console.log("Engine started");
}
abstract drive(): void;
}
class Car extends Vehicle {
drive(): void {
console.log("Car is driving");
}
}
// Use an interface for a contract without a hierarchical relationship
interface Serializable {
serialize(): string;
}
class User implements Serializable {
serialize(): string {
return JSON.stringify(this);
}
}
This guide illustrates the distinctions and applications of abstract classes and interfaces in TypeScript, providing a solid foundation for understanding when to use each in your designs.