DEV Community

Cover image for [Part 2] A Brief Introduction to SOLID Typescript
Taki089.Dang
Taki089.Dang

Posted on • Originally published at dev.to

[Part 2] A Brief Introduction to SOLID Typescript

I - Interface Segregation Principle (ISP)

Clients should not be forced to implement interfaces they do not use.

This means interfaces should be small and specific to the needs of the implementing class.
For Example:

❌Bad

interface Work{
    eat(): void;
    work(): void;
}

class Developer implements Work{
    eat() {
        console.log("eat");
    }
    work() {
        console.log("work");
    }
}
class Robot implements Work{
    eat() {
        throw new Error("eat"); // Robot does not need to inherit eat() method
    }
    work() {
        console.log("work");
    }
}
Enter fullscreen mode Exit fullscreen mode

✅Good

interface Workable  {
    work(): void;
}
interface Eatable {
    eat(): void;
}
class Developer implements Workable, Eatable {
    eat() {}

    work(): void {
    }
}
Enter fullscreen mode Exit fullscreen mode

Base on the strength of interface, one class can implement multiple interfaces. You should break the interface into smaller parts to better suit the need of your class


D - Dependency Inversion Principle (DIP)

*High-level modules should not depend on low-level modules. Both should depend on abstractions.
*

This priciple encourage the use of dependency injection
For Example:

❌Bad

class BackendDeveloper {
    develop(){
        console.log("Developing developer");
    }
}
class FrontendDeveloper {
    develop(){
        console.log("FrontendDeveloper");
    }
}
class Project {
    backendDeveloper: BackendDeveloper;
    frontendDeveloper: FrontendDeveloper;
    constructor() {
        this.backendDeveloper = new BackendDeveloper();
        this.frontendDeveloper = new FrontendDeveloper();
    }
    build(){
        this.backendDeveloper.develop();
        this.frontendDeveloper.develop();
    }
}
Enter fullscreen mode Exit fullscreen mode

In the snippet of code, there are multiple problem.However, the most important problem you can see is:

  • If you want to add MobileDeveloper(), you will need to modify Project() which will impact other method using it
  • Project() is tightly couple to backendDeveloper and frontendDeveloper

✅Good

interface Developer {
    developer(): void
}
class BackendDev implements Developer {
    developer() {
        console.log("Developer Developer");
    }
}
class FrontendDeveloper implements Developer {
    developer() {
        console.log("Developer Developer");
    }
}
class Project {
    constructor(private developers: Developer[]) {
    }
    build(){
        this.developers.forEach(developer => {developer.developer();});
    }
}

Enter fullscreen mode Exit fullscreen mode

High-level module depends on abstractions (Developer interface).
Low-level modules implement the abstraction.
Loose coupling makes it easy to extend and maintain.

Adding MobileDev:

class MobileDeveloper implements Developer {
  develop() {
    console.log("Writing mobile code");
  }
}

// Usage
const developers: Developer[] = [
  new BackendDeveloper(),
  new FrontendDeveloper(),
  new MobileDeveloper(), //easy to add and make it does not change in Project()
];
const project = new Project(developers);
project.build();

Enter fullscreen mode Exit fullscreen mode

Thank You :)))

Top comments (0)