Разбираем миксины в TypeScript
Development, Java | Комментировать запись
В TypeScript нельзя наследовать или расширять более одного класса, но миксины позволяют обойти это ограничение.
Миксины создают разделяемые классы, которые можно объединять и, таким образом, формировать единый класс, содержащий все методы и свойства.
Примечание: В документации TypeScript подход, описанный в этом руководстве, называется альтернативным шаблоном.
В этом руководстве мы поговорим о том, как создавать и использовать миксины в TypeScript.
Ограничения классов
Чтобы продемонстрировать ценность миксинов, мы создадим следующий пример.
Предположим, у нас есть два класса, Car и Lorry, которые содержат методы drive и carry соответственно. Давайте рассмотрим третий класс под названием Truck. Он должен включать в себя оба метода, drive и carry:
export class Car { drive(name:string) { console.log(`This ${name} can drive very fast`); } } export class Lorry { carry(weight:number) { console.log(`This vehicle can carry ${weight} kg`); } } export class Truck extends Car, Lorry {}
Этот код не сработает, потому расширить можно только один класс.
error: Classes can only extend a single class
Чтобы решить эту проблему, мы можем использовать миксины.
Расширение класса интерфейса и слияние объявлений
Чтобы создать миксин, мы воспользуемся двумя функциями TypeScript. В этом разделе мы подробнее поговорим о них.
Расширение класса интерфейса
В отличие от классов, интерфейсы в TypeScript могут расширять несколько классов.
interface A extends ClassB, ClassC {}
Когда интерфейс расширяет класс, он расширяет только члены класса, но не их реализацию (потому что интерфейсы не содержат реализации).
Слияние объявлений
Когда вы создаете два или более объявления с одним и тем же именем, TypeScript объединяет их в одно.
interface Alligator { eyes: number; nose: number; } interface Alligator { tail: number; } const gator: Alligator = { eyes: 2, nose: 1, tail: 1 };
gator содержит свойства обоих интерфейсов Alligator.
Вспомогательная функция
Используя две вышеописанные функции TypeScript, мы можем создать интерфейс по имени Truck, и расширить классы Car и Lorry:
export class Truck {} export interface Truck extends Car, Lorry {}
Из-за слияния объявлений класс Truck объединится с интерфейсом Truck. Это означает, что класс Truck теперь будет содержать определения функций из классов Car и Lorry.
Чтобы позволить классу Truck реализовать унаследованные от Car и Lorry функции, мы воспользуемся вспомогательной функцией из документации TypeScript.
В качестве первого аргумента функция принимает имя класса, в который мы хотим скопировать реализации (в нашем случае это Truck). В качестве второго аргумента она принимает массив классов, из которых мы хотим скопировать реализации (в нашем примере это Car и Lorry).
// the helper function function applyMixins(derivedCtor: any, constructors: any[]) { constructors.forEach((baseCtor) => { Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => { Object.defineProperty( derivedCtor.prototype, name, Object.getOwnPropertyDescriptor(baseCtor.prototype, name) || Object.create(null) ); }); }); }
Получается вот что:
applyMixins(Truck, [Car, Lorry]);
Теперь мы можем получить доступ к методам в Car и Lorry из объекта truck.
const truck = new Truck(); truck.drive("truck"); truck.carry(10);
Этот код выдаст следующий результат:
This truck can drive very fast This vehicle can carry 10 kg
Заключение
В этом мануале мы рассмотрели, как обойти одно из важных ограничений в TypeScript при помощи миксинов.
Читайте также: Преимущества TypeScript
Tags: TypeScript