Previous Parts: Clean Code: Functions and Methods in TypeScript [Part 1] Clean Code: Naming and Code Composition in TypeScript [Part 2] Clean Code: Classes and Objects in TypeScript [Part 3] Clean Code: Single Responsibility, Open/Closed, Liskov Substitution SOLID Principles in TS [Part 4] Table of Contents Interface Segregation Principle Dependency Inversion Principle In this article, we continue considering SOLID principles and practical examples of bad and clean architectural solutions. 1. Interface Segregation Principle Classes shouldn’t be forced to depend on methods they do not use The basics of this principle suggest that if any classes/objects do not implement any parts of the interface these fields/methods should be moved to the other interface. Let’s take a look at an example: enum Facets { CompanyFacet: 'companyFacet', TopicFacet: 'topicFacet' } // Bad interface Facet { id: string; countNews: number; countDocs: number; selected: boolean; companyName?: string; topicName?: string; } function checkFacetType(facet: Facet): string { if (facet.companyName) { return Facet.CompanyFacet; } if (facet.topicName) { return Facet.TopicFacet; } } In the current example, the company facet doesn’t have the field , and vice versa. So we need to move fields and to specified interfaces. The same rule will be for classes and methods. If any inherited class will not realize or use any parent method this method should be moved to a separated class/interface. topicName companyName topicName // Better export interface Facet { id: string; countNews: number; countDocs: number; selected: boolean; } interface CompanyFacet extends Facet { companyName: string; } interface CompanyFacet extends Facet { topicName: string; } 2. Dependency Inversion Principle High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend upon details. Details should depend on abstractions. // Bad class DataBaseClient { request(): Promise<IClient> { return Promise.resolve(); } } class LocalStorageClient { getItem(): IClient { return localStorage.getItem('any'); } } class ClientRequest { private request: DataBaseClient; constructor() { this.request = new DataBaseClient(); } get(): IClient { this.request.request(); } } const client = new ClientRequest().get(); Here we have class (high-level module) with which we request the client info from the backend. In this class, we have a strict dependency on (low-level module). If we decide to start grabbing clients from we will need to rewrite the realization of class, substitute to and follow the new interface. It causes a lot of risk because we changed the realization of high-level class. ClientRequest DataBaseClient localStorage ClientRequest DataBaseClient LocalStorageClient // We change DataBaseClient to LocalStorageClient class ClientRequest { private request: LocalStorageClient; constructor() { this.request = new LocalStorageClient(); } get(): IClient { this.request.getItem(); } } const client = new ClientRequest().get(); To reduce code coupling and make high-level classes independent from low-level classes we need to pass an abstract parameter as a dependency in a high-level module following a particular interface. // Better interface Client { get(): IClient; } class DataBaseClient implements Client { get(): IClient{ return Promise.resolve().then(/../); } } class LocalStorageClient implements Client { get(): IClient{ return localStorage.getItem('any'); } } class ClientRequest { constructor(client: Client ) {} get(): IClient { return client.get(); } } const client = new ClientRequest(new LocalStorageClient()).get(); Here we created an interface . Our high-level class takes parameters following the interface with the method . And we substitute dependency without code rewriting. So now high-level class doesn’t depend on low-level classes or . Client ClientRequest client Client get ClientRequest ClientRequest LocalStorageClient DataBaseClient // Change the dependency const request = new ClientRequest(new LocalStorageClient().get()); Conclusion With this article, we are done considering clean code principles in TypeScript. In the next article we will take a look at clean code approaches in Angular using TypeScript and following the already discussed TS principles.