Applying the Open/Closed Principle in TypeScript
Expanding existing functionality in software can quickly lead to rigid, fragile codebases if the system isn’t designed for extension. The Open/Closed Principle (OCP)—one of the core SOLID principles—aims to solve this problem. It states that:
- Modules should be open for extension (you can introduce new behavior)
- But closed for modification (existing code should stay untouched)
In this guide, you’ll learn what the principle means in practice, why it matters, and how to implement it cleanly in TypeScript using the Strategy Pattern.
What Is the Open/Closed Principle?
The OCP, formulated by Bertrand Meyer, ensures that software can evolve without constantly rewriting stable, tested code.
Why this matters:
- Scalability — new features don’t break old ones.
- Testability — logic is split into small, isolated units.
- Maintainability — fewer code changes mean fewer bugs.
- Flexibility — behavior is controlled by interchangeable components.
A Bad Example: Violating the Open/Closed Principle
Let’s start with an example that doesn’t follow OCP:
function calculate(a: number, b: number, op: string): number {
switch (op) {
case 'add':
return a + b;
case 'subtract':
return a - b;
default:
throw new Error('Unsupported operation');
}
}This requires modifying the function whenever new operations are added — violating OCP.
Applying the Open/Closed Principle with the Strategy Pattern
1. Strategy Interface
interface CalculationStrategy {
execute(a: number, b: number): number;
}2. Concrete Strategies
class AddStrategy implements CalculationStrategy {
execute(a: number, b: number): number {
return a + b;
}
}
class SubtractStrategy implements CalculationStrategy {
execute(a: number, b: number): number {
return a - b;
}
}3. Calculator Class
class Calculator {
constructor(private strategy: CalculationStrategy) {}
setStrategy(strategy: CalculationStrategy) {
this.strategy = strategy;
}
calculate(a: number, b: number): number {
return this.strategy.execute(a, b);
}
}4. Adding New Behavior Without Modifying Existing Code
class MultiplyStrategy implements CalculationStrategy {
execute(a: number, b: number): number {
return a * b;
}
}Usage Example
const calculator = new Calculator(new AddStrategy());
calculator.calculate(6, 2); // 8
calculator.setStrategy(new SubtractStrategy());
calculator.calculate(6, 2); // 4
calculator.setStrategy(new MultiplyStrategy());
calculator.calculate(6, 2); // 12Benefits
- Extensibility without modifying existing logic
- Cleaner architecture
- Reusable strategy modules
- Safer scaling
Conclusion
The Open/Closed Principle helps create scalable and maintainable TypeScript applications. Using the Strategy Pattern ensures new features can be introduced safely without touching proven, existing code.