Ответ 1
Я закончил играть с декораторами и решил записать то, что я понял для всех, кто хочет воспользоваться этим, прежде чем выйдет какая-либо документация. Не стесняйтесь редактировать это, если увидите какие-либо ошибки.
Общие очки
- Декораторы вызывается при объявлении класса, а не при создании объекта.
- Несколько декораторов могут быть определены в одном классе /Property/Method/Parameter.
- Декораторы не допускаются к конструкторам.
Действительный декоратор должен быть:
- Назначение одного из типов Decorator (
ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecorator
).- Возвращает значение (в случае декораторов классов и декоратора), которое присваивается украшенному значению.
Метод/Формальный декоратор
Параметры реализации:
-
target
: прототип класса (Object
). -
propertyKey
: имя метода (string
|symbol
). -
descriptor
:TypedPropertyDescriptor
Если вы не знакомы с дескрипторными ключами, я бы рекомендовал прочитать об этом в этой документации поObject.defineProperty
(это третий параметр).
Пример - без аргументов
Использование:
class MyClass {
@log
myMethod(arg: string) {
return "Message -- " + arg;
}
}
Реализация:
function log(target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) {
const originalMethod = descriptor.value; // save a reference to the original method
// NOTE: Do not use arrow syntax here. Use a function expression in
// order to use the correct value of 'this' in this method (see notes below)
descriptor.value = function(...args: any[]) {
// pre
console.log("The method args are: " + JSON.stringify(args));
// run and store result
const result = originalMethod.apply(this, args);
// post
console.log("The return value is: " + result);
// return the result of the original method (or modify it before returning)
return result;
};
return descriptor;
}
Входные данные:
new MyClass().myMethod("testing");
Вывод:
Аргументы метода: ["testing"]
Возвращаемое значение: Message-testing
Заметки:
- Не используйте синтаксис стрелки при настройке значения дескриптора. Контекст
this
не будет экземпляром, если вы это сделаете. - Лучше изменить исходный дескриптор, чем перезаписать текущий, вернув новый дескриптор. Это позволяет использовать несколько декораторов, которые редактируют дескриптор, не перезаписывая то, что сделал другой декоратор. Это позволяет вам использовать что-то вроде
@enumerable(false)
и@log
одновременно (пример: ) { return { value: function (...args: any[]) { var a = args.map(a => JSON.stringify(a)).join(); var result = value.value.apply(this, args); var r = JSON.stringify(result); console.log(`Call: ${propertyKey}(${a}) => ${r}`); return result; } }; } function enumerable(isEnumerable: boolean) { return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor) => { descriptor.enumerable = isEnumerable; return descriptor; } } var test = new FooBar(); test.foo("asdf"); rel=noreferrer>Bad vs ) { var originalMethod = value.value; value.value = function (...args: any[]) { var a = args.map(a => JSON.stringify(a)).join(); var result = originalMethod.apply(this, args); var r = JSON.stringify(result); console.log(`Call: ${propertyKey}(${a}) => ${r}`); return result; }; return value; } function enumerable(isEnumerable: boolean) { return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor) => { descriptor.enumerable = isEnumerable; return descriptor; } } var test = new FooBar(); test.foo("asdf"); rel=noreferrer>Good) - Полезно: аргумент типа
TypedPropertyDescriptor
может использоваться для ограничения того, какие сигнатуры методов ( number>) { console.log('This descriptor is only allowed on methods that have one parameter of type number and return a number.'); return descriptor; } rel=noreferrer>пример метода) или сигнатуры доступа () { console.log('This descriptor is only allowed on accessors that return a number.'); return descriptor; } rel=noreferrer>пример Accessor) может быть помещен в декоратор.
Пример - с аргументами (фабрика декораторов)
При использовании аргументов вы должны объявить функцию с параметрами декоратора, а затем вернуть функцию с сигнатурой примера без аргументов.
class MyClass {
@enumerable(false)
get prop() {
return true;
}
}
function enumerable(isEnumerable: boolean) {
return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) => {
descriptor.enumerable = isEnumerable;
return descriptor;
};
}
Декоратор статического метода
Как и у декоратора с некоторыми отличиями:
- Его
target
параметр - это сама конструкторская функция, а не прототип. - Дескриптор определяется как функция конструктора, а не прототип.
Класс декоратора
@isTestable
class MyClass {}
Параметр реализации:
-
target
: класс, на который объявлен декоратор (TFunction extends Function
).
Пример использования: использование метаданных api для хранения информации о классе.
Декоратор объектов
class MyClass {
@serialize
name: string;
}
Параметры реализации:
-
target
: прототип класса (Object
). -
propertyKey
: имя свойства (string
|symbol
).
Пример использования: Создание @serialize("serializedName")
и добавление имени свойства в список свойств для сериализации.
Декоратор параметров
class MyClass {
myMethod(@myDecorator myParameter: string) {}
}
Параметры реализации:
-
target
: прототип класса (Function
-it кажется, чтоFunction
больше не работает. Теперь вы должны использоватьany
илиObject
здесь, чтобы использовать декоратор в любом классе. Или укажите типы (-и) класса, которые вы хотите ограничить к) -
propertyKey
: имя метода (string
|symbol
). -
parameterIndex
: индекс параметра в списке параметров функции (number
).
Подробный пример (ы)
- Memoize decorator - Метод, Get/Set Пример декоратора Accessor