Object-oriented programming (OOP) is a paradigm that allows developers to structure their code in a more organized and modular way, making it easier to manage and maintain. In this article, we’ll explore some fundamental concepts of OOP—inheritance, polymorphism, abstraction, and encapsulation—through the lens of TypeScript, a popular statically typed superset of JavaScript. We’ll delve into each concept with examples to illustrate their usage and benefits.
Inheritance
Inheritance is a mechanism in OOP that allows a class (subclass) to inherit properties and behaviors (methods) from another class (superclass). This promotes code reuse and establishes a hierarchy among classes. In TypeScript, inheritance is achieved using the extends keyword.
constructor(public name: string) {}
move(distanceInMeters: number = 0) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
bark() {
console.log(“Woof! Woof!”);
}
}
dog.bark(); // Output: Woof! Woof!
dog.move(10); // Output: Buddy moved 10m.
In this example, the Dog class inherits the move method from the Animal class and extends it with its own method bark.
Polymorphism
Polymorphism allows objects of different classes to be treated as objects of a common superclass. It enables flexibility and extensibility in code by allowing methods to be overridden in subclasses. TypeScript supports polymorphism through method overriding.
area(): number {
return 0;
}
}
constructor(private radius: number) {
super();
}
area(): number {
return Math.PI * this.radius ** 2;
}
}
constructor(private width: number, private height: number) {
super();
}
area(): number {
return this.width * this.height;
}
}
shapes.forEach(shape => {
console.log(“Area:”, shape.area());
});
In this example, both Circle and Rectangle classes override the area method of the Shape class to calculate their respective areas.
Abstraction
Abstraction is the process of hiding complex implementation details and exposing only the necessary functionalities to the outside world. Abstract classes and methods in TypeScript facilitate abstraction.
constructor(public name: string) {}
abstract move(): void;
}
move() {
console.log(`${this.name} is driving.`);
}
}
move() {
console.log(`${this.name} is flying.`);
}
}
const plane = new Plane(“Boeing”);
car.move(); // Output: Toyota is driving.
plane.move(); // Output: Boeing is flying.
Here, Vehicle is an abstract class with an abstract method move(). Subclasses Car and Plane provide concrete implementations of the move method.
Encapsulation
Encapsulation is the bundling of data (attributes) and methods that operate on that data into a single unit (class). It restricts direct access to the data from outside the class and promotes data hiding and abstraction. In TypeScript, encapsulation is achieved through access modifiers like public, private, and protected.
private id: number;
public name: string;
constructor(id: number, name: string) {
this.id = id;
this.name = name;
}
displayInfo() {
console.log(`ID: ${this.id}, Name: ${this.name}`);
}
}
console.log(emp.name); // Output: John Doe
emp.displayInfo(); // Output: ID: 101, Name: John Doe
// console.log(emp.id); // Error: Property ‘id’ is private and only accessible within class ‘Employee’.
In this example, id is a private member of the Employee class, accessible only within the class itself.
Conclusion
Understanding and applying the principles of inheritance, polymorphism, abstraction, and encapsulation are crucial for writing clean, maintainable, and scalable code. TypeScript’s support for these OOP concepts enhances code readability and modularity, making it a powerful choice for building complex applications. By leveraging these features effectively, developers can write more robust and extensible software solutions.