Декораторы используются для некоторого аннотирования классов, методов, свойств, или параметров, чтобы использовать некоторое метапрограммирование.
Декораторы - это функции, которые оборачивают нужный нам функционал и обогащают логику работы программы (или данного функционала)
083 Паттерн декоратора
Паттерн декоратора - это определённая методология, которая предлагает нам оборачивать методы, классы, свойства и параметры в функции, которые модифицируют поведение этих объектов. При этом можно производить композицию данных декораторов.
Сама функциональность декораторов в языке просто позволяет удобнее использовать данный паттерн.
Паттерн представляет собой использование функции, в которую вкладывается тип объекта (в примере - интерфейс) и ретёрнется этот же объект, но модифицированный.
По сути своей, работаем извне мы уже над модифицированным объектом
А вот и объяснение, почему декораторы на классах работают снизу вверх: верхний декоратор покрывает выполнение нижнего
Декораторы, в своей роли, выступают некоторым синтаксическим сахаром, который позволяет более удобно реализовывать данный паттерн
084 Декоратор класса
Чтобы начать использовать декораторы, нужно включить их в ТС
Сама функция декоратора принимает в себя таргет, типом которого является функция.
Чтобы изменить значения объекта, нужно обратиться к нему через прототип (потому что таргет - это функция)
Декораторы используются до того, как создастся объект, поэтому если внутри класса будет присвоение числа, то оно сохранится и не изменится декоратором. Поэтому все изменения и присвоения значений должны происходить внутри декоратора
Так же у нас есть второй вариант создания функции-декоратора - это объявление конструируемого класса (дженерик T) и возвращение анонимного класса, который расширяется от получаемого класса
Этот вариант имеет больший приоритет над первым вариантом и оригинальным объектом. Тут мы непосредственно работаем с переменными декорируемого класса и эти изменения будут задаваться даже поверх тех значений, что находятся внутри класса (в отличие от первого способа, где приоритет оригинального класса выше изменения прототипов функции)
085 Фабрика декораторов
Фабрика декораторов - это функция, которая производит нужные нам декораторы.
Так же в фабрику мы можем превратить нашу функцию для конструирования класса
И тут стоит упомянуть, в каком порядке выполняются декораторы. Порядок инициализации - прямой, а уже исполнение происходит в обратном порядке.
086 Упражнение - Декоратор CreatedAt
Нам нужно создать декоратор CreatedAt который будет добавлять в класс дату создания его инстанса
Однако мы можем встретиться с такой проблемой, что обратиться непосредственно к сгенерированному свойству или методу у нас не получится извне (так как ТС ориентируется ещё и на рантайм-код)
Поэтому, чтобы решить проблему с недоступными извне свойствами, нам нужно создать отдельный тайп, который будет явно указывать, что у нас есть нужное нам свойство или метод в инстансе класса
087 Декоратор метода
Структура декоратора метода выглядит таким образом:
Сначала принимает в себя метод
Потом наименование метода
Потом дескриптор метода (который хранит всю важную для нас информацию)
Далее возвращаем дескриптор или войд от декоратора
И уже дальше можем прописать дополнительную логику, которая должна выполняться до самого метода (выполнится декоратор потом метод)
Что мы можем менять через дескриптор? Мы можем задать модификаторы для метода: его конфигурируемость, читабелность, поменять значения, задать get/set
Так же мы можем менять логику выполнения функции. Через обращение к значению дескриптора, мы можем задать новое значение для нашей функции и логика выполнения этой функции так же поменяется.
На примере можно увидеть, что теперь вместо Error функция выдаёт 'no error'
Мы можем не только переопределять значения функции, но и дополнять их. Можно заранее сохранить значение дескриптора и вызвать его во время изменения, чтобы дополнить логику нашего метода (а не перетереть её)
Так же ничто нам не мешает сделать фэктори декоратор и для метода вот таким вот образом:
088 Упражнение - Декоратор перехвата ошибок
Нам нужно создать декоратор, который будет обрабатывать ошибку и выводить её в зависимости от значения аргумента rethrow
И тут сразу хочется отметить. что если мы оставим async/await, то у нас в выводе будет фигурировать Promise
089 Декоратор свойства
Декораторы свойств уже представляют из себя функции, которые позволят нам валидировать входящие значения, вводить новые ограничения для свойств, назначать свои геттеры и сеттеры и в принципе дополнять логику для обычных свойств таким образом, чтобы мы могли явно регулировать их.
Декоратор свойства отличается от декоратора метода только тем, что в него мы уже не передаём дескрипторы (их нужно будет редактировать через defineProperty).
Цель такая: нам нужно написать декоратор свойства, который задаст максимальное значение для свойства = 100
Первым делом, нам нужно описать геттеры и сеттеры для свойства, чтобы была возможность регулировать задание нового значения и получение
Так же мы можем через defineProperty поменять дескрипторы для свойств и заменить стоковые методы (get/set) и модификаторы (читабельность, изменяемость и так далее) для этих свойств
Теперь мы спокойно можем пользоваться нужным нам функционалом и значение больше 100 задать не сможем - получим предупреждение
090 Декоратор accessor
Декоратор accessor используется не так часто, как другие декораторы. Конкретно он покрывает выполнение геттеров и сеттеров. Нам достаточно написать данный метод один раз над одним аццессором и он будет покрывать сразу оба (на оба навешивать не надо - это будет ошибкой)
Дескриптор хранит ту же самую информацию, что и дескриптор для методов
И конкретно тут мы описали логику модификации сеттера (геттер работает по прежнему)
set?.apply(target, args) - применяем сеттер на таргете (нашем классе) и вкладываем в качестве аргумента принимаемые значения
091 Декоратор параметра
Декораторы параметров зачастую используются для метапрограммирования
092 Метаданные
Для начала нам нужно активировать метаданные в ТС, и уже затем после компиляции мы будем получать нативный JS с метаданными, которые б
И далее нужно установить саму библиотеку для работы с метаданными
И через Reflection мы получаем возможность использовать методы для обработки метаданных. Метод getOwnMetadata() принимает в себя три аргумента: тип запрашиваемых метаданных, класс и наименование метода внутри этого класса. Типы метаданных мы можем найти в документации.
Используется для валидации типов или для связывания компонентов (dependency injection)
Метод Positive навешивает в метаданные каждого аргумента метку, что значение позитивно и оно нам подходит
И тут уже показана реализация валидации аргументов метода. Сначала мы присваиваем марку валидируемости для одного значения, а уже через Validate делаем общую проверку валидных значений
093 Порядок декораторов
Первыми у нас всегда срабатывают свойства (вне зависимости от положения в коде).
Дальше срабатывает инициализация метода и его параметров. А уже вызов происходит в обратном порядке.
Дальше происходит тот же самый порядок со статиками.
И уже в конце инициализируется класс с его параметром. В обратном порядке они выполняются
Порядок расположения кода влияет только на одинаковые уровни инициализации - они выполняются по порядку в коде