DEV Community

Cover image for [Part 2] A Brief Introduction to SOLID Typescript
Taki (Kieu Dang)
Taki (Kieu Dang)

Posted on • Originally published at dev.to

1

[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 :)))

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (0)

The Most Contextual AI Development Assistant

Pieces.app image

Our centralized storage agent works on-device, unifying various developer tools to proactively capture and enrich useful materials, streamline collaboration, and solve complex problems through a contextual understanding of your unique workflow.

👥 Ideal for solo developers, teams, and cross-company projects

Learn more