В 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 для одних и тех же вещей существует несколько разных инструментов, мы можем реализовывать классы с помощью расширения абстрактных классов вместо интерфейсов. Но выбор будет зависеть от задачи. Абстрактные классы предоставляют нам модификаторы доступа и конструкторы, в то время как интерфейсы более легковесны и просты.
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Вебинар «Как самостоятельно учиться»
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.