implements: реализация интерфейсов

Ключевое слово implements в TypeScript указывает, что класс обязуется реализовать все члены интерфейса; компилятор проверяет соответствие и выдаёт ошибку при отсутствии или несовместимости любого из них.

Зачем нужно

implements позволяет отделить контракт (интерфейс) от реализации (класс). Это основа принципов SOLID (особенно ISP и DIP): код зависит от интерфейсов, а не от конкретных классов, что упрощает замену реализаций и тестирование через mock.

Где используется

  • Repository, Service, Gateway паттерны
  • Dependency Injection — классы реализуют интерфейсы зависимостей
  • Тестирование — mock-классы реализуют те же интерфейсы
  • Плагин-системы — каждый плагин реализует общий интерфейс
  • Стратегии и адаптеры

Основной контент

Базовый синтаксис

interface Greeter {
  greet(name: string): string;
  farewell(name: string): string;
}

class EnglishGreeter implements Greeter {
  greet(name: string): string {
    return `Hello, ${name}!`;
  }
  farewell(name: string): string {
    return `Goodbye, ${name}!`;
  }
}

class RussianGreeter implements Greeter {
  greet(name: string): string {
    return `Привет, ${name}!`;
  }
  farewell(name: string): string {
    return `Пока, ${name}!`;
  }
}

// Использование через интерфейс — не зависим от конкретного класса
function greetUser(greeter: Greeter, user: string): void {
  console.log(greeter.greet(user));
}

Реализация нескольких интерфейсов

interface Serializable {
  serialize: string;
}

interface Validatable {
  validate: boolean;
}

class Order implements Serializable, Validatable {
  constructor(
    private id: string,
    private items: string,
    private total: number,
  ) {}

  serialize: string {
    return JSON.stringify({ id: this.id, items: this.items, total: this.total });
  }

  validate: boolean {
    return this.items.length > 0 && this.total > 0;
  }
}

Repository паттерн

interface UserRepository {
  findById(id: string): Promise<User | null>;
  findAll: Promise<User>;
  save(user: User): Promise<User>;
  delete(id: string): Promise<void>;
}

// Реальная реализация
class PostgresUserRepository implements UserRepository {
  async findById(id: string) { /* SQL запрос */ return null; }
  async findAll { return ; }
  async save(user: User) { return user; }
  async delete(id: string) { /* ... */ }
}

// Mock для тестов
class InMemoryUserRepository implements UserRepository {
  private users = new Map<string, User>;

  async findById(id: string) { return this.users.get(id) ?? null; }
  async findAll { return [...this.users.values()]; }
  async save(user: User) { this.users.set(user.id, user); return user; }
  async delete(id: string) { this.users.delete(id); }
}

implements vs extends

// extends — наследование, получаем реализацию родителя
class Dog extends Animal { /* ... */ }

// implements — только контракт, без наследования реализации
class Labrador implements Animal { /* нужно реализовать всё */ }

// Можно комбинировать
class SpecialDog extends Dog implements Serializable {
  serialize: string { return JSON.stringify(this); }
}

implements с generic интерфейсом

interface Repository<T> {
  findById(id: string): Promise<T | null>;
  save(entity: T): Promise<T>;
}

class OrderRepository implements Repository<Order> {
  async findById(id: string): Promise<Order | null> { return null; }
  async save(order: Order): Promise<Order> { return order; }
}

Частые ошибки

  • Пропустить хотя бы один метод интерфейса — компилятор выдаст ошибку с указанием отсутствующего члена.
  • Несовместимая сигнатура — метод greet(n: number) не реализует greet(n: string) из интерфейса.
  • implements не влияет на совместимость в рантайме — TypeScript использует структурную типизацию; instanceof не проверяет implements.
  • Путать implements и extendsimplements берёт только контракт, extends — реализацию; нельзя implements от класса без abstract.

Связанные темы

Ресурсы