Exploring Object-Oriented Programming – TypeScript Examples

RMAG news

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.

class Animal {
constructor(public name: string) {}

move(distanceInMeters: number = 0) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}

class Dog extends Animal {
bark() {
console.log(“Woof! Woof!”);
}
}
const dog = new Dog(“Buddy”);
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.

class Shape {
area(): number {
return 0;
}
}
class Circle extends Shape {
constructor(private radius: number) {
super();
}

area(): number {
return Math.PI * this.radius ** 2;
}
}

class Rectangle extends Shape {
constructor(private width: number, private height: number) {
super();
}

area(): number {
return this.width * this.height;
}
}

const shapes: Shape[] = [new Circle(5), new Rectangle(4, 6)];

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.

abstract class Vehicle {
constructor(public name: string) {}

abstract move(): void;
}

class Car extends Vehicle {
move() {
console.log(`${this.name} is driving.`);
}
}
class Plane extends Vehicle {
move() {
console.log(`${this.name} is flying.`);
}
}
const car = new Car(“Toyota”);
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.

class Employee {
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}`);
}
}

const emp = new Employee(101, “John Doe”);
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.