Перейти к содержанию

Миксины (примеси)

TypeScript (и JavaScript) классы поддерживают строго одиночное наследование. К примеру, вы не можете сделать следующее:

1
2
3
class User extends Tagged, Timestamped {
    // ОШИБКА : невозможно множественное наследование
}

Еще один способ создания классов из повторно используемых компонентов - путем объединения более простых частичных классов, называемых миксины.

Идея проста: вместо класса A, расширяющего класс B для получения своей функциональности, функция B принимает класс A и возвращает новый класс с этой добавленной функциональностью. Функция B - это миксин.

[Миксин] - это функция, которая

  1. берет конструктор
  2. создает класс, расширяющий этот конструктор новыми функциями
  3. возвращает новый класс

Подробный пример:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// Требуется для всех миксинов
type Constructor<T = {}> = new (...args: any[]) => T;

////////////////////
// Примеры миксинов
////////////////////

// Миксин, который добавляет свойство
function Timestamped<TBase extends Constructor>(
    Base: TBase
) {
    return class extends Base {
        timestamp = Date.now();
    };
}

// миксин, который добавляет свойство и методы
function Activatable<TBase extends Constructor>(
    Base: TBase
) {
    return class extends Base {
        isActivated = false;

        activate() {
            this.isActivated = true;
        }

        deactivate() {
            this.isActivated = false;
        }
    };
}

////////////////////
// Использование для создания классов
////////////////////

// Простой класс
class User {
    name = '';
}

// Пользователь с отметкой времени
const TimestampedUser = Timestamped(User);

// Пользователь с отметкой времени и доступный для активации
const TimestampedActivatableUser = Timestamped(
    Activatable(User)
);

////////////////////
// Использование созданных классов
////////////////////

const timestampedUserExample = new TimestampedUser();
console.log(timestampedUserExample.timestamp);

const timestampedActivatableUserExample = new TimestampedActivatableUser();
console.log(timestampedActivatableUserExample.timestamp);
console.log(timestampedActivatableUserExample.isActivated);

Разложим этот пример на части.

Возьмите конструктор

Миксины берут класс и расширяют его новой функциональностью. Итак, нам нужно определить, что такое конструктор. Просто как:

1
2
// Требуется для всех миксинов
type Constructor<T = {}> = new (...args: any[]) => T;

Расширить класс и вернуть его

Довольно просто:

1
2
3
4
5
6
7
8
// Миксин, который добавляет свойство
function Timestamped<TBase extends Constructor>(
    Base: TBase
) {
    return class extends Base {
        timestamp = Date.now();
    };
}

И это все 🌹

Комментарии