DEV Community

Abhinav
Abhinav

Posted on

Understanding OOP in JavaScript and TypeScript 🖥️✨

Object-Oriented Programming (OOP) is like giving your code a superpower to think and act like real-world objects 🦸‍♂️. Whether you're working with a person, an animal, or even a bank account, OOP helps you structure your code in a way that's modular, reusable, and easy to understand.

JavaScript (JS) and TypeScript (TS) fully embrace OOP, giving you tools to create robust and scalable applications. In this blog, we’ll dive into the core concepts of OOP and see how they work in JS and TS! 🧑‍💻


What is OOP? 🤔

OOP is all about organizing your code around objects. Objects have:

  • Properties: Think of these as attributes or characteristics (e.g., a dog has a name 🐶).
  • Methods: Actions the object can perform (e.g., a dog can bark 🗣️).

At its core, OOP revolves around these principles:

  1. Encapsulation: Keeping the details private, sharing only what’s necessary 🔒.
  2. Abstraction: Hiding complexity to make things simple 🧩.
  3. Inheritance: Passing down traits and behaviors from parent to child 📦.
  4. Polymorphism: Having multiple forms — one interface, many implementations 🎭.

OOP in JavaScript 🌟

JavaScript wasn't originally designed with OOP in mind, but it evolved to embrace it! Let’s explore the key features:

1. Classes in JavaScript 🏗️

A class is like a blueprint. You define a class, and then you can create multiple objects (instances) from it.

class Animal {
    constructor(name) {
        this.name = name; // Property
    }

    speak() {
        console.log(`${this.name} makes a sound.`);
    }
}

const dog = new Animal("Dog");
dog.speak();  // Output: "Dog makes a sound."
Enter fullscreen mode Exit fullscreen mode
  • constructor: This special method runs when you create a new instance (new Animal()).
  • Methods: Functions inside a class define an object’s behavior.

2. Inheritance in JavaScript 🧬

With inheritance, one class can inherit properties and methods from another.

class Dog extends Animal {
    speak() {
        console.log(`${this.name} barks!`);
    }
}

const myDog = new Dog("Buddy");
myDog.speak();  // Output: "Buddy barks!"
Enter fullscreen mode Exit fullscreen mode

Here, Dog inherits from Animal but overrides the speak method. This is polymorphism in action!


3. Encapsulation in JavaScript 🔐

Encapsulation hides data and provides access through methods. JavaScript introduced private fields (#) to ensure data isn’t directly accessible.

class BankAccount {
    #balance = 0;

    deposit(amount) {
        this.#balance += amount;
        console.log(`Deposited: ${amount}`);
    }

    getBalance() {
        return this.#balance;
    }
}

const account = new BankAccount();
account.deposit(100);       // Deposited: 100
console.log(account.getBalance()); // Output: 100
// account.#balance;          // Error: Private field '#balance' is not accessible
Enter fullscreen mode Exit fullscreen mode

OOP in TypeScript 💪

TypeScript takes JavaScript’s OOP to the next level by adding types and access modifiers.

1. Type-Safe Classes in TypeScript ✅

With TypeScript, you can define the type of each property and method to avoid runtime errors.

class Animal {
    name: string;

    constructor(name: string) {
        this.name = name;
    }

    speak(): void {
        console.log(`${this.name} makes a sound.`);
    }
}

const cat = new Animal("Cat");
cat.speak();  // Output: "Cat makes a sound."
Enter fullscreen mode Exit fullscreen mode
  • name: string ensures name is always a string.
  • speak(): void ensures the method doesn’t return a value.

2. Access Modifiers 🔓

TypeScript adds public, private, and protected to control access to class members:

  • public: Accessible everywhere (default).
  • private: Accessible only within the class.
  • protected: Accessible within the class and its subclasses.
class Person {
    public name: string;
    private age: number;
    protected city: string;

    constructor(name: string, age: number, city: string) {
        this.name = name;
        this.age = age;
        this.city = city;
    }

    greet(): void {
        console.log(`Hi, I'm ${this.name}.`);
    }
}

class Student extends Person {
    study(): void {
        console.log(`${this.name} is studying in ${this.city}.`);
    }
}

const student = new Student("Alice", 20, "New York");
student.greet();  // Output: "Hi, I'm Alice."
// student.age;    // Error: 'age' is private
Enter fullscreen mode Exit fullscreen mode

3. Abstract Classes and Interfaces 🧩

TypeScript lets you create abstract classes and interfaces for advanced use cases. Abstract classes provide a blueprint, while interfaces define contracts.

abstract class Shape {
    abstract area(): number; // Must be implemented in subclasses

    describe(): void {
        console.log("I am a shape.");
    }
}

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

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

const circle = new Circle(5);
circle.describe();          // Output: "I am a shape."
console.log(circle.area()); // Output: 78.54
Enter fullscreen mode Exit fullscreen mode

Why Use OOP? 🤷‍♂️

OOP simplifies code management and makes applications scalable. Here’s why you should use it:

  • 📦 Modular Code: Break down complex tasks into small, manageable parts.
  • 🔄 Reusability: Inherit and reuse logic, reducing redundancy.
  • 🛠️ Maintainability: Encapsulation makes it easier to modify code without breaking it.
  • 📈 Scalability: Add new features effortlessly with inheritance and polymorphism.

Final Thoughts 🚀

OOP is the backbone of modern programming, and both JavaScript and TypeScript provide excellent support for it. Whether you're building small apps or large enterprise systems, mastering OOP will make your code cleaner, more maintainable, and easier to scale.

Start using OOP in your next project, and experience the magic of organized and reusable code! 🎉

Top comments (0)