JavaScript & TypeScriptМодуль 8: Продвинутый TypeScript
Декораторы
Декораторы классов и методов
Цель урока
В этом уроке ты научишься:
- Создавать декораторы классов
- Создавать декораторы методов
- Использовать декораторы свойств
Экспериментальная функция
Декораторы — экспериментальная функция. Включи experimentalDecorators в tsconfig.json.
Настройка
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}Декоратор класса
function Logger(constructor: Function) {
console.log("Создан класс: " + constructor.name);
}
@Logger
class User {
constructor(public name: string) {}
}
// При загрузке: "Создан класс: User"Фабрика декораторов
function Logger(prefix: string) {
return function(constructor: Function) {
console.log(prefix + ": " + constructor.name);
};
}
@Logger("LOG")
class User {
constructor(public name: string) {}
}
// "LOG: User"Декоратор метода
function Log(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const original = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Вызов ${propertyKey} с аргументами:`, args);
const result = original.apply(this, args);
console.log(`Результат:`, result);
return result;
};
}
class Calculator {
@Log
add(a: number, b: number): number {
return a + b;
}
}
let calc = new Calculator();
calc.add(2, 3);
// Вызов add с аргументами: [2, 3]
// Результат: 5Декоратор свойства
function MinLength(length: number) {
return function(target: any, propertyKey: string) {
let value: string;
const getter = () => value;
const setter = (newValue: string) => {
if (newValue.length < length) {
throw new Error(`${propertyKey} должен быть минимум ${length} символов`);
}
value = newValue;
};
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter
});
};
}
class User {
@MinLength(3)
name: string;
constructor(name: string) {
this.name = name;
}
}
let user = new User("Иван"); // OK
// let user2 = new User("Ив"); // Ошибка!Декоратор параметра
function Required(
target: any,
propertyKey: string,
parameterIndex: number
) {
console.log(`Параметр ${parameterIndex} в ${propertyKey} обязателен`);
}
class User {
greet(@Required name: string) {
console.log("Привет, " + name);
}
}Порядок выполнения
function First() {
console.log("First factory");
return function(target: any) {
console.log("First decorator");
};
}
function Second() {
console.log("Second factory");
return function(target: any) {
console.log("Second decorator");
};
}
@First()
@Second()
class Example {}
// First factory
// Second factory
// Second decorator (снизу вверх)
// First decoratorПрактический пример
function Autobind(
_target: any,
_propertyKey: string,
descriptor: PropertyDescriptor
) {
const original = descriptor.value;
return {
configurable: true,
enumerable: false,
get() {
return original.bind(this);
}
};
}
class Button {
label = "Нажми меня";
@Autobind
handleClick() {
console.log(this.label);
}
}
let button = new Button();
let handler = button.handleClick;
handler(); // "Нажми меня" (this привязан)Практика
Задание 1: Декоратор класса
Задача: Создай декоратор, добавляющий timestamp.
Loading...
Ваш вывод:
Задание 2: Декоратор метода
Задача: Создай декоратор для измерения времени.
Loading...
Ваш вывод:
Проверь себя
- Как включить декораторы?
- В каком порядке выполняются декораторы?
- Какие типы декораторов существуют?