Understanding SOLID Principles in TypeScript - Featured Image
Programming4 min read

Understanding SOLID Principles in TypeScript

Today, let's talk about SOLID principles in a simple way. Many developers know about them, but using them correctly can be tricky. I'll explain each principle with TypeScript examples to make it easy to understand.

Let’s get started!

1. Single Responsibility Principle (SRP)

The Single Responsibility Principle states that a class should have only one reason to change. In other words, each class should focus on a single responsibility.

Bad example (Violating SRP)

class TaskManager {
  constructor() {}
  connectAPI(): void {}
  createTask(): void {
    console.log("Create Task");
  }
  updateTask(): void {
    console.log("Update Task");
  }
  removeTask(): void {
    console.log("Remove Task");
  }
  sendNotification(): void {
    console.log("Send Notification");
  }
  sendReport(): void {
    console.log("Send Report");
  }
}

Here, TaskManager is handling too many responsibilities, including API connection, task management, and notifications. This makes the code harder to maintain.

Good example (Following SRP)

class APIConnector {
  connectAPI(): void {}
}

class Report {
  sendReport(): void {
    console.log("Send Report");
  }
}

class Notificator {
  sendNotification(): void {
    console.log("Send Notification");
  }
}

class TaskManager {
  createTask(): void {
    console.log("Create Task");
  }
  updateTask(): void {
    console.log("Update Task");
  }
  removeTask(): void {
    console.log("Remove Task");
  }
}

Now, each class has a single responsibility, making it easier to manage and scale.


2. Open-Closed Principle (OCP)

The Open-Closed Principle states that a class should be open for extension but closed for modification. This means you should be able to add new functionality without modifying existing code.

Bad example (Violating OCP)

type ExamType = {
  type: "BLOOD" | "XRay";
};

class ExamApprove {
  approveRequestExam(exam: ExamType): void {
    if (exam.type === "BLOOD") {
      console.log("Blood Exam Approved");
    } else if (exam.type === "XRay") {
      console.log("XRay Exam Approved");
    }
  }
}

If we need to add a new exam type, we must modify the class, which violates OCP.

Good example (Following OCP)

interface ExamApprove {
  approveRequestExam(): void;
}

class BloodExam implements ExamApprove {
  approveRequestExam(): void {
    console.log("Blood Exam Approved");
  }
}

class XRayExam implements ExamApprove {
  approveRequestExam(): void {
    console.log("XRay Exam Approved");
  }
}

Now, we can add new exam types without modifying existing classes, making the system scalable.


Liskov Substitution Principle (LSP)

The Liskov Substitution Principle states that a subclass should be able to replace its parent class without causing issues.

Bad example (Violating LSP)

class Student {
  constructor(public name: string) {}
  study(): void {
    console.log(`${this.name} is studying`);
  }
  deliverThesis(): void {}
}

class PostgraduateStudent extends Student {
  // Postgraduate students don’t deliver a thesis, but this method is still inherited
}

Here, PostgraduateStudent inherits a method it doesn’t need, which violates LSP.

Good example (Following LSP)

class Student {
  constructor(public name: string) {}
  study(): void {
    console.log(`${this.name} is studying`);
  }
}

class GraduateStudent extends Student {
  deliverThesis(): void {
    console.log("Thesis delivered");
  }
}

Now, only relevant methods exist in each class.


4. Interface Segregation Principle (ISP)

The Interface Segregation Principle states that a class should not be forced to implement methods it doesn’t use.

Bad example (Violating ISP)

interface Employee {
  salary(): number;
  generateCommission(): void;
}

class Receptionist implements Employee {
  salary(): number {
    return 1000;
  }
  generateCommission(): void {
    // Receptionists don’t get commissions
  }
}

Receptionists don’t need generateCommission(), but they are forced to implement it.

Good example (Following ISP)

interface Employee {
  salary(): number;
}

interface Commissionable {
  generateCommission(): void;
}

class Seller implements Employee, Commissionable {
  salary(): number {
    return 1000;
  }
  generateCommission(): void {
    console.log("Generating Commission");
  }
}

class Receptionist implements Employee {
  salary(): number {
    return 1000;
  }
}

Now, only the necessary methods are implemented by each class.


5. Dependency Inversion Principle (DIP)

The Dependency Inversion Principle states that high-level modules should not depend on low-level modules; both should depend on abstractions.

Bad example (Violating DIP)

class OrderRepository {
  saveOrder(order: Order) {}
}

class OrderService {
  private orderRepository = new OrderRepository();
  processOrder(order: Order) {
    this.orderRepository.saveOrder(order);
  }
}

Here, OrderService is tightly coupled with OrderRepository.

Good example (Following DIP)

interface OrderRepository {
  saveOrder(order: Order): void;
}

class DatabaseRepository implements OrderRepository {
  saveOrder(order: Order) {
    console.log("Saving order");
  }
}

class OrderService {
  constructor(private orderRepository: OrderRepository) {}
  processOrder(order: Order) {
    this.orderRepository.saveOrder(order);
  }
}

Now, OrderService depends on an abstraction, making it flexible to changes.


Conclusion

By following SOLID principles, you make your code more maintainable, scalable, and easier to understand. These best practices help you write clean code that is easier to extend and modify in the future.

I hope this guide made SOLID principles easier for you! Happy coding!

hassaankhan789@gmail.com

Frontend Web Developer

Posted by





Subscribe to our newsletter

Join 2,000+ subscribers

Stay in the loop with everything you need to know.

We care about your data in our privacy policy

Background shadow leftBackground shadow right

Have something to share?

Write on the platform and dummy copy content

Be Part of Something Big

Shifters, a developer-first community platform, is launching soon with all the features. Don't miss out on day one access. Join the waitlist: