正在加载,请稍候…

SOLID Principles in TypeScript: Practical Examples

Apply SOLID principles in real TypeScript projects. Learn Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, and Dependency Inversion with concrete examples.

SOLID Principles in TypeScript: Practical Examples

SOLID principles guide you toward maintainable, flexible object-oriented design.

Single Responsibility Principle (SRP)

Each class should have one reason to change.

// Bad: UserService does too many things
class UserService {
  saveUser(user: User) { /* DB logic */ }
  sendWelcomeEmail(user: User) { /* Email logic */ }
  generateReport(userId: string) { /* Report logic */ }
}

// Good: Separate concerns
class UserRepository {
  async save(user: User): Promise<void> { /* DB logic */ }
  async findById(id: string): Promise<User | null> { /* DB logic */ }
}

class EmailService {
  async sendWelcome(user: User): Promise<void> { /* Email logic */ }
}

class UserReportService {
  async generate(userId: string): Promise<Report> { /* Report logic */ }
}

Open/Closed Principle (OCP)

Open for extension, closed for modification.

// Bad: Adding new discount type requires modifying existing code
class DiscountCalculator {
  calculate(type: string, price: number): number {
    if (type === 'seasonal') return price * 0.9;
    if (type === 'member') return price * 0.85;
    // Every new type requires modifying this class
    return price;
  }
}

// Good: Extend via new implementations
interface DiscountStrategy {
  apply(price: number): number;
}

class SeasonalDiscount implements DiscountStrategy {
  apply(price: number): number { return price * 0.9; }
}

class MemberDiscount implements DiscountStrategy {
  apply(price: number): number { return price * 0.85; }
}

class PriceCalculator {
  constructor(private discount: DiscountStrategy) {}
  calculate(price: number): number {
    return this.discount.apply(price);
  }
}

Liskov Substitution Principle (LSP)

Subtypes must be substitutable for their base types.

// Bad: Square breaks Rectangle behavior
class Rectangle {
  setWidth(w: number) { this.width = w; }
  setHeight(h: number) { this.height = h; }
  area(): number { return this.width * this.height; }
  protected width = 0;
  protected height = 0;
}

class Square extends Rectangle {
  setWidth(w: number) { this.width = this.height = w; } // violates LSP!
  setHeight(h: number) { this.width = this.height = h; }
}

// Good: Separate abstractions
interface Shape {
  area(): number;
}

class Rectangle implements Shape {
  constructor(private width: number, private height: number) {}
  area(): number { return this.width * this.height; }
}

class Square implements Shape {
  constructor(private side: number) {}
  area(): number { return this.side ** 2; }
}

Interface Segregation Principle (ISP)

Don't force clients to depend on interfaces they don't use.

// Bad: Fat interface
interface Worker {
  work(): void;
  eat(): void;
  sleep(): void;
}

// Good: Segregated interfaces
interface Workable {
  work(): void;
}

interface Eatable {
  eat(): void;
}

interface Sleepable {
  sleep(): void;
}

class HumanWorker implements Workable, Eatable, Sleepable {
  work() { /* ... */ }
  eat() { /* ... */ }
  sleep() { /* ... */ }
}

class Robot implements Workable {
  work() { /* ... */ }
  // Robots don't need eat/sleep
}

Dependency Inversion Principle (DIP)

Depend on abstractions, not concretions.

// Bad: High-level depends on low-level
class OrderService {
  private db = new MySQLDatabase(); // concrete dependency!
  async createOrder(order: Order): Promise<void> {
    await this.db.insert('orders', order);
  }
}

// Good: Depend on abstraction
interface Database {
  insert(table: string, data: unknown): Promise<void>;
  query<T>(sql: string, params: unknown[]): Promise<T[]>;
}

class OrderService {
  constructor(private db: Database) {} // inject abstraction
  async createOrder(order: Order): Promise<void> {
    await this.db.insert('orders', order);
  }
}

// Wire up in composition root
const db = new PostgreSQLDatabase(config);
const orderService = new OrderService(db);

SOLID principles work together to create systems that are easy to test, extend, and maintain.