DEV Community

Cover image for Understanding Object-Oriented Programming in TypeScript
coder7475
coder7475

Posted on

Understanding Object-Oriented Programming in TypeScript

What is OOP?

Object-Oriented Programming (OOP) is a programming paradigm that organizes software around "objects." These objects represent real-world entities, combining data (properties) and behavior (methods) into a single unit. OOP enhances code reusability, scalability, and maintainability, making it a cornerstone of modern software development.

In this blog, we'll explore the core building blocks of OOP—Inheritance, Polymorphism, Abstraction, and Encapsulation—using TypeScript examples. We'll also discuss Statics as an additional OOP concept.


Building Blocks of OOP

OOP revolves around four fundamental principles:

  • Inheritance: Allows a class to inherit properties and methods from another class.
  • Polymorphism: Enables objects of different types to be treated as instances of a common type.
  • Abstraction: Hides complex details and exposes only the essentials.
  • Encapsulation: Restricts access to an object’s internal data, ensuring controlled interaction.

Let’s dive into each with practical TypeScript examples.


Inheritance

Inheritance enables a class (child) to inherit properties and methods from another class (parent), promoting code reuse and relationships between classes.

Example: Student and Teacher

The Teacher class inherits from Student, gaining its properties and methods while adding its own.

class Student {
  constructor(public name: string, public age: number, public address: string) {}

  getSleep(hours: number) {
    console.log(`${this.name} will sleep for ${hours}`);
  }
}

class Teacher extends Student {
  constructor(name: string, age: number, address: string, public designation: string) {
    super(name, age, address);
  }

  takeClass(hours: number) {
    console.log(`${this.name} takes ${hours} of class each day`);
  }
}

const student1 = new Student('asif', 30, 'Dhaka');
student1.getSleep(5); // asif will sleep for 5

const teacher1 = new Teacher('Tarek', 34, 'Chittagong', 'Math Teacher');
teacher1.getSleep(7); // Tarek will sleep for 7
teacher1.takeClass(3); // Tarek takes 3 of class each day
Enter fullscreen mode Exit fullscreen mode

The super keyword initializes the parent class, ensuring proper setup.


Polymorphism

Polymorphism allows objects of different classes to be treated as instances of a shared superclass, enabling flexible and reusable code.

Example: Sleeping Hours

Here, Student and Developer override the getSleep method from Person, yet all work with a single function.

class Person {
  getSleep() {
    console.log("A person sleep for 8 hours!");
  }
}

class Student extends Person {
  getSleep() {
    console.log("A person sleep for 7 hours!");
  }
}

class Developer extends Person {
  getSleep() {
    console.log("A person sleep for 6 hours!");
  }
}

function getSleepingHours(instance: Person) {
  return instance.getSleep();
}

const abul = new Person();
const kabul = new Student();
const habul = new Developer();

getSleepingHours(abul);  // A person sleep for 8 hours!
getSleepingHours(kabul); // A person sleep for 7 hours!
getSleepingHours(habul); // A person sleep for 6 hours!
Enter fullscreen mode Exit fullscreen mode

This demonstrates how polymorphism unifies behavior across subclasses.


Abstraction

Abstraction simplifies systems by hiding implementation details and exposing only necessary interfaces. TypeScript supports this with interfaces and abstract classes.

Example: Vehicles

An interface Vehicle1 defines a contract, while an abstract class Vehicle2 provides a base with abstract methods.

// Interface: purely defines a contract
interface Vehicle1 {
  startEngine(): void;
  stopEngine(): void;
}

// Abstract class: defines abstract methods
abstract class Vehicle2 {
  abstract startEngine(): void;
  abstract move(): void;
  abstract stopEngine(): void;
}

// Class implementing the interface
class Car1 implements Vehicle1 {
  startEngine(): void {
    console.log("starting engine...");
  }
  stopEngine(): void {
    console.log("stopping engine");
  }
}

// Class extending the abstract class
class Car2 extends Vehicle2 {
  startEngine(): void {
    console.log("starting engine");
  }
  move(): void {
    console.log("Moving car");
  }
  stopEngine(): void {
    console.log("stopping engine..");
  }
}

const car1 = new Car1();
car1.startEngine(); // starting engine...
car1.stopEngine();  // stopping engine

const car2 = new Car2();
car2.startEngine(); // starting engine
car2.move();        // Moving car
car2.stopEngine();  // stopping engine..
Enter fullscreen mode Exit fullscreen mode

Car1 implements Vehicle1, while Car2 extends Vehicle2, fulfilling their contracts.


Encapsulation

Encapsulation protects an object’s internal state using access modifiers (public, protected, private) and getters/setters.

Example: Bank Account

The BankAcc class controls access to its data:

class BankAcc {
  constructor(
    readonly id: string,
    protected name: string,
    private _balance: number
  ) {}

  addDeposit(amount: number) {
    this._balance += amount;
  }

  get balance() {
    return this._balance;
  }

  set deposit(amount: number) {
    this._balance += amount;
  }
}

class StudentAcc extends BankAcc {
  getName() {
    return this.name; // Accessible because it’s protected
  }
}

const acc1 = new BankAcc('111', 'rob', 42000);
acc1.addDeposit(33);
console.log(acc1.balance); // 42033

const s1 = new StudentAcc('222', 'sifat', 43);
console.log(s1.getName()); // sifat
s1.deposit = 1;
console.log(s1.balance);   // 44
Enter fullscreen mode Exit fullscreen mode
  • id is readonly, immutable post-creation.
  • name is protected, accessible to subclasses.
  • _balance is private, managed via methods.

Statics

Static members belong to the class itself, not instances, and are shared across all objects.

Example: Counter

class Counter {
  static count: number = 0;

  increment() {
    return (Counter.count += 1);
  }

  decrement() {
    return (Counter.count -= 1);
  }
}

const instance1 = new Counter();
console.log(instance1.increment()); // 1

const instance2 = new Counter();
console.log(instance2.increment()); // 2

console.log(instance1.decrement()); // 1
Enter fullscreen mode Exit fullscreen mode

The count property is static, maintaining a single value across instances.


Conclusion

OOP in TypeScript provides powerful tools to structure code effectively. Through Inheritance, Polymorphism, Abstraction, Encapsulation, and Statics, you can build robust, reusable, and maintainable applications. Experiment with these examples to master OOP principles in your projects!

Top comments (0)