Что позволяет использовать полиморфизм в NestJS?

«Что позволяет использовать полиморфизм в NestJS?» — вопрос из категории NestJS, который задают на 26% собеседований Node.js Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

NestJS, построенный на TypeScript, активно использует полиморфизм через свою систему внедрения зависимостей (DI). Это позволяет создавать гибкие и легко тестируемые приложения.

  1. Абстрактные классы и наследование. Можно определить общую логику в базовом классе и переопределять её в наследниках.

    export abstract class CacheService {
      abstract get(key: string): Promise<string>;
      abstract set(key: string, value: string): Promise<void>;
    }
    
    @Injectable()
    export class RedisCacheService extends CacheService {
      async get(key: string) { /* ...реализация для Redis */ }
      async set(key: string, value: string) { /* ... */ }
    }
    
    @Injectable()
    export class InMemoryCacheService extends CacheService {
      private cache = new Map();
      async get(key: string) { return this.cache.get(key); }
      // ...
    }

    Затем в провайдерах модуля можно указать, какую реализацию использовать:

    { provide: CacheService, useClass: RedisCacheService }
  2. Интерфейсы и их токены. Поскольку интерфейсы TypeScript не существуют во время выполнения, NestJS использует строковые или символьные токены для связи абстракции с реализацией.

    export const PAYMENT_SERVICE = Symbol('IPaymentService');
    export interface IPaymentService {
      charge(amount: number): Promise<Receipt>;
    }
    
    @Injectable()
    export class StripeService implements IPaymentService { /* ... */ }
    
    // В модуле
    providers: [
      { provide: PAYMENT_SERVICE, useClass: StripeService }
    ]
    
    // В конструкторе сервиса
    constructor(@Inject(PAYMENT_SERVICE) private paymentService: IPaymentService) {}
  3. Динамические провайдеры (useFactory, useExisting). Позволяют на лету решать, какую конкретную реализацию инжектировать, основываясь на конфигурации или контексте.

    {
      provide: 'CONNECTION',
      useFactory: (configService: ConfigService) => {
        if (configService.get('USE_PRODUCTION_DB')) {
          return new ProductionDatabaseConnection();
        }
        return new MockDatabaseConnection();
      },
      inject: [ConfigService]
    }

Таким образом, полиморфизм в NestJS — это краеугольный камень архитектуры, позволяющий легко подменять реализации, писать модульные тесты и соблюдать принцип инверсии зависимостей (DIP).