Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Реализация интерфейсов классами Продвинутый Typescript

В TypeScript классы могут тесно взаимодействовать с интерфейсами. В прошлом уроке мы увидели, как интерфейсы могут расширять другие интерфейсы и комбинировать их. Аналогичным образом интерфейсы могут быть расширены классами:

interface IBeep {
  sayBeep: () => string;
}

interface IBoop {
  sayBoop: () => string;
}

class Robo implements IBeep, IBoop {
  sayBeep = () => 'beep';

  sayBoop = () => 'boop';
}

const R2D2 = new Robo();
R2D2.sayBeep(); // 'beep'

Здесь мы реализовали два интерфейса с помощью класса, который унаследовал все методы данных интерфейсов. Унаследованные методы нам пришлось прописать вручную.

Мы можем создавать классы на основе интерфейсов так же, как мы создаем интерфейсы на основе интерфейсов. Но есть и отличия.

Если мы создаем интерфейс или тип и потом транспилируем TypeScript в JavaScript, то в коде не останется образца этого интерфейса. В то же время при создании класса его образец всегда переносится и в JavaScript при транспиляции.

Получается, что вариант с интерфейсами более легковесный, но все же выбор должен зависеть от нашей решаемой задачи.

Создание класса на основе интерфейса не ведет к точной реализации этого интерфейса в классе. TypeScript просто проверяет, удовлетворяют ли свойства и методы нашего класса свойствам, заявленным в интерфейсе. Сам же класс мы пишем вручную. Рассмотрим пример:

interface ICalculate {
  sum: (num1: number, num2: number ) => number;
}

class Summator implements ICalculate {
  sum(num1, num2) { return num1 + num2; }
  // Для параметров будет выведено сообщение: Parameter 'num1'/'num2' implicitly has an 'any' type
  // Так происходит, потому что TypeScript только проверяет класс на соответствие интерфейсу

  multiply(num1: number, num2: number) { return num1 * num2; }
  // Мы добавили новый метод, но TypeScript не ругается
}

let calculator = new Summator();
  // Наш код сработает, как если бы он сработал для аргументов с типом any, потому что
  // типы параметров и все остальное не были унаследованы классом при реализации интерфейса
calculator.sum(2,3) // 5

Ошибка в реализации интерфейса классом возможна только тогда, когда мы не реализуем одно из свойств, указанных в интерфейсе. Или мы реализуем его не так, как указано в интерфейсе:

interface ICalculate {
  sum: (num1: number, num2: number ) => number;
}

class Summator implements ICalculate {
  sum (num1: string, num2: string) { return num1 + num2 };
  // Мы изменили типы аргументов на string, то есть неверно реализовали интерфейс
  // В таком случае TypeScript обратит внимание на нашу ошибку и не скомпилируется
  // Type '(num1: string, num2: string) => string' is not assignable to type '(num1: number, num2: number) => number'
}

По этой же причине, если мы пишем класс, реализующий интерфейс с опциональными свойствами, нам нужно прописывать все самостоятельно. В противном случае эти свойства не попадут в наш класс:

interface ICalculate {
  sum: (num1: number, num2: number) => number;
  multiply? : (num1: number, num2: number) => number;
}

class Summator implements ICalculate {
  sum (num1: number, num2: number) { return num1 + num2; }
}

const calculator = new Summator();
calculator.sum(2,3) // 5
calculator.multiply(2,3) // Property 'multiply' does not exist on type 'Summator'.

В примере выше мы указали только метод sum при реализации интерфейса классом Summator. В результате код успешно скомпилировался, ведь метод multiply был указан как опциональный. В то же время в экземпляре нашего класса мы не можем обратиться к этому методу.

Поскольку в TypeScript для одних и тех же вещей существует несколько разных инструментов, мы можем реализовывать классы с помощью расширения абстрактных классов вместо интерфейсов. Но выбор будет зависеть от задачи. Абстрактные классы предоставляют нам модификаторы доступа и конструкторы, в то время как интерфейсы более легковесны и просты.


Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
от 25 000 ₸ в месяц
Разработка фронтенд-компонентов для веб-приложений
10 месяцев
с нуля
Старт 28 ноября
профессия
от 39 525 ₸ в месяц
Разработка фронтенд- и бэкенд-компонентов для веб-приложений
16 месяцев
с нуля
Старт 28 ноября
профессия
от 25 000 ₸ в месяц
Разработка бэкенд-компонентов для веб-приложений
10 месяцев
с нуля
Старт 28 ноября

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»