Каждый Ангуляр разработчик видел декораторы в тайпскрипт коде. Их используют, чтобы описать Модули, сконфигурировать Dependency Injection или настроить компонент. Другими словами, декораторы используются, чтобы описать дополнительную информацию, или метаданные, для фреймворка или компилятора (в случае Ангуляра). При чем, Ангуляр лишь один из примеров. Существуют многие другие библиотеки, использующие декораторы для простоты и наглядности кода, как декларативный подход. Как .NET разработчик в прошлом, я вижу много сходства между TS декораторами и .NET аттрибутами. Наконец, набирающий популярность NestJS фреймворк для бекенд приложений (абстракция над Node), также построен на интенсивном использовании декораторов и декларативном подходе. Как это все работает и каким образом использовать декораторы в своем коде, чтобы он был более удобным и читабельным? Мы все понимаем, что после компиляции TS кода мы получаем Javascript код. В котором нет понятия декоратор, как и многих других Typescript особенностей. Поэтому для меня наиболее интересным является вопрос, во что превращается декоратор после компиляции. Занимаясь этим вопросом, я сделал выступление на митапе в Минске и хочу поделиться статьей.   Наиболее яркие на мой взгляд примеры работы с декораторами можно найти в Ангуляре. Давайте взглянем на пару из них, перед погружением в технические подробности. Из примеров видно, что чаще всего декораторы позволяют добавить полезной информации, метаданных, классу.  Чтобы начать пользоваться декораторами, стоит проверить tsconfig.json файл, в нем должны быть включены опции emitDecoratorMetadata и experimentalDecorators, так как это все еще экспериментальная функциональность. Согласно документации, Декоратор — это специальный вид описания, который можно присоединить к декларации класса, метода, get свойства, свойства или параметра. Декораторы используют форму @expression, то есть при использовании ставится символ @ перед именем декоратора. Хотя по сути expression может быть любая функция. Эта функция будет вызвана в процессе выполнения программы, причем вызывающий код добавит аргументы с информацией о том объекте, который был задекорирован. Другими словами, декоратор — это способ добавить дополнительное поведение классу, функции, свойству или параметру. Это можно отнестик парадигме мета-программирования или декларативного программирования. Важно, что декоратор — это лишь функция. При использовании, среда исполнения сначала вызовет функцию-декоратор, и только потом будет выполнен основной сценарий объекта (если код декоратора содержит этот вызов). При наличии нескольких декораторов, они будет вызваны по очереди, сверху вниз. Начнем с наиболее очевидного случая — декоратора для функции. Определение в самом Typescript выглядит следующим образом: Это функция, принимающая несколько аргументов. А именно:  Дескриптор выглядит так: По сути, дескриптор нужен, чтобы получить доступ к исходной функции и иметь возможность ее вызвать из кода декоратора. Стоит отметить, что функция-декоратор будет вызвана не вашим кодом, компилятор сам подставит в нее нужные аргументы. В примере чуть ниже мы посмотрим скомпилированный пример Javascript кода.  Чтобы рассмотреть пример, нам понадобится какой-нибудь понятный и полезный сценарий. Например — измерение производительности функции. Для нашего сценария код декоратора может выглядеть таким образом: Как я сказал ранее, декоратор — это функция, которая возвращает функцию определенного типа. В примере видны аргументы этой функции — target, propertyName и дескриптор функции. Их компилятор подставит в вызывающий код.  Дескриптор функции здесь позволяет переопределить поведение — подменить искомую функцию на новую, которая уже следует заданной декоратором логике. Наша логика подразумевает засечь момент старта функции, и ее завершения, и вывести разницу в консоль. Конечно не стоит забывать вернуть значение искомой функции. Скомпилированнй Javascript код будет выглядеть следующим образом Тут никаких сюрпризов, все примерно как и в Typescript коде. А вот код вызывающий уже интереснее: Тут уже видна системная функция __decorate, в которую передается наш декоратор вместе с дополнительными аргументами.  Подставленный компилятором код, вызывающий функцию __decorate, будет выполнен в процессе интерпретации кода, сразу после интерпретации класса. Но сам код нашего декоратора будет вызываться каждый раз, когда вызывается исходная функция. Это ключевое отличие от следующего вида декораторов. Этот вид декораторов как правило используется, чтобы добавить классу метаданных. Где и как они будут использованы — уже другой вопрос. В Ангуляре — это подсказки компилятору. Но есть и более понятные сценарии — например Dependency Injection. Давайте напишем свой простой и легкий контейнер зависимостей на основе декоратора класса. Например, мы бы могли его использовать следующим образом: Прежде чем приступить, посмотрим формальное определение декоратора класса в Typescript: Таким образом наш декоратор будет выглядить таким образом: Мы определили интерфейс для придания структуры нашим метаданным. Самая важная для нас информация — является ли данный класс singelton-ом или же его можно инстанциировать многократно. Дальше мы просто сохраняем данную информацию для дальнейшего использования.  Пара важных моментов: Reflect-metadata это хранилище метаданных в Typescript. Его смысл тот же, что и в других языках — хранить информацию о типах для работы с ней в процессе выполнения программы. В нашем случае, мы добавили свои метаданные класса, которыми будем пользоваться в своем контейнере зависимостей. Вуаля, в несколько сравнительно простых строк кода мы написали свой контейнер зависимостей и декоратор для определения, может ли класс иметь несколько экземпляров или только один. Теперь можем где угодно в коде вызывать getInstance, а как может быть создан класс уже прописано в его декораторе. Еще одна область применения декораторов относится к свойствам класса. Тут открывается целый спектр прикладных задач, но наиболее насущной, на мой взгляд, является валидация данных. Представьте, есть класс Person с полем Age, значения которого по логике приложения должно быть между 18 и 60. Давайте сделаем данную проверку с помощью декоратора: Снова обратимся к формальному определению: Наш декоратор для валидации выглядит следующим образом: И снова мы видим, что основной логики тут нет. Мы просто сохраняем нужную нам информацию в хранилище метаданных. Все потому, что это код, как и код декоратора класса, будет выполнен только один раз при прочтении кода. До того, как конструктор класса был вызван. Скомпилированный код: Видим, что сразу после определения класса компилятор поместил свою функцию __decorate, в которую передал наш декоратор с параметрами. Это своеобразное подтверждение того, что основная задача декораторов — сделать код более удобным к прочтению, информативно богатым. В случае валидации — описать правила проверок в том же месте, где и сам класс, причем в удобной форме. Возвращаясь к валидации, ее необходимо описать отдельно: В примере, конечно же, мы делаем одну единственную проверку. Реальный сценарий будет несколько сложнее. Пример вызова: Если вы дочитали до этого места, я уверен, что вы получили достаточно информации и понимания, чтобы сделать маленькую домашнюю работу и разобраться в декораторах для параметров функций самостоятельно. Формально же, декоратор для параметра функции выглядит таким образом: Я бы хотел привести библиотеку Class-Validator, использование которой для меня лично очень удобно. Ее декораторы постоянно используются в коде моих проектов. Думаю, из примера все понятно. Прочие же детали можно найти в репозитории. Интересный факт в том, что именно эта библиотека используется по умолчанию в фреймворке NestJS когда применяется @UsePipes(new ValidationPipe()) для валидации всех входящих http запросов. Потенциал Typescript по созданию удобного, простого для прочтения и надежного кода очень велик. Его можно использовать сразу в нескольких парадигмах, в том числе для мета-программирования. Декораторы, даже будучи экспериментальной функциональностью, дают возможности для решения целого спектра прикладных задач, помогают сделать код простым для прочтения и более удобным для работы, помогают в решении таких задач как логирование, измерение производительности, проверки, дополнительное поведение… Чем и пользуются такие фреймворки как Angular и NestJS. Понимание декораторов помогает писать код более красиво (пусть это и субьективная оценка). Пишите код, улучшайте его, вычишайте его, делайте его красивым, тестируйте и наконец наслаждайтесь как проделанной работой, так и самим процессом создания кода! p.s. пока писал (вернее переводил свою же статью), нашел еще одну тут же на хабре, которая хорошо раскрывает тему декораторов. Привожу ссылку Full Stack Software Developer