GAZAR

Principal Engineer | Mentor

Open-Closed Principle (OCP) Best Practices in TypeScript: Guidelines and Examples

Open-Closed Principle (OCP) Best Practices in TypeScript: Guidelines and Examples

The Open-Closed Principle (OCP) is a fundamental principle of object-oriented design that states that software entities (classes, modules, functions, etc.) should be open for extension but closed for modification. In TypeScript development, adhering to the OCP helps create code that is flexible, maintainable, and easy to extend without altering existing code.

Key OCP Best Practices:

  • Abstraction and Interfaces:

Encapsulate behaviour using abstract classes and interfaces, defining contracts that specify the behaviour expected from concrete implementations. By programming to interfaces rather than concrete implementations, you can easily extend functionality without modifying existing code.

interface Shape {
  area(): number;
}

class Circle implements Shape {
  constructor(private radius: number) {}
  area(): number {
    return Math.PI * this.radius ** 2;
  }
}
  • Dependency Injection:

Inject dependencies into classes or functions through constructor injection or method injection, allowing for the substitution of implementations at runtime. By decoupling dependencies from concrete implementations, you enable the application to be extended with new functionality without modifying existing code.

class Logger {
  log(message: string): void {
    console.log(message);
  }
}

class ProductService {
  constructor(private logger: Logger) {}
  saveProduct(product: Product): void {
    this.logger.log(`Product saved: ${product.name}`);
  }
}
  • Strategy Pattern:

Use the strategy pattern to encapsulate algorithms and allow them to be selected at runtime. By defining a family of algorithms and encapsulating each one into a separate class, you can easily introduce new algorithms without modifying existing code.

(https://gazar.dev/design-pattern/strategy-design-pattern)

interface SortingStrategy {
  sort(items: any[]): any[];
}

class QuickSort implements SortingStrategy {
  sort(items: any[]): any[] {
  }
}

class MergeSort implements SortingStrategy {
  sort(items: any[]): any[] {
  }
}
  • Extension Points:

Design classes and modules with extension points that allow for customization and extension without modification. Use hooks, events, or plugins to provide extension points where additional functionality can be plugged in.

class Button {
  onClick(callback: () => void): void {
  }
}
const button = new Button();
button.onClick(() => {});

By following OCP best practices in TypeScript development, developers can create code that is flexible, extensible, and easy to maintain. By embracing abstraction, dependency injection, the strategy pattern, and extension points, developers can build software systems that can evolve and adapt to changing requirements without sacrificing stability or introducing regressions.