Декораторы — инструмент декларативного программирования. С их помощью можно легко и элегантно добавить к классам и членам класса метаданные. На основе этих метаданных можно расширять или изменять поведения классов и членов класса, не изменяя при этом кодовую базу, к которой применен декоратор. Саму технологию можно отнести к мета-программированию или декларативному программированию. В рамках этой статьи разбирается несколько примеров из реальных проектов, где применение декораторов сильно упростило код для понимания и исключило его дублирование. С помощью декораторов мы можем избежать “дублирования” кода, инкапсулировав сквозную функциональность в отдельный модуль. Убрать лишний “шум” в коде, что позволит сфокусироваться автору на бизнес логике приложения. Сквозная функциональность  —  функциональность, которая распределена по всей кодовой базе. Как правило, эта функциональность не зависит от предметной области вашего проекта. К ней можно отнести следующие примеры: Логирование Кеширование Валидация Форматирование и т.д. Для работы со сквозной функциональностью существует целая парадигма — Аспектно-ориентированное программирование (AOP). Про ее реализацию в JavaScript советую прочитать в этой замечательной статье. Так же существуют замечательные библиотеки, реализующие AOP в JavaScript: aspect.js meld Если Вам интересна тема AOP, советую поставить эти пакеты и поиграться с их функциональностью.  В этой статье я попытался показать, как можно решить описанные выше проблемы встроенной в TypeScript функциональностью — декораторы. Для прочтения этой статьи предполагается, что вы уже имеет опыт использования react, mobx и typescript, т.к. я не буду вдаваться в подробности этих технологий. В TypeScript декоратором является функция. Форма применения: @funcName. Где funcName— имя функции описывающее декоратор. После прикреплении декоратора к члену класса, а затем его вызове, сначала будут выполняться декораторы, а затем уже код класса. Однако декоратор может прервать поток выполнения кода на своем уровне, так что, основной код класса в конечном счете не будет выполнен. Если к члену класса прикреплены несколько декораторов, их выполнение происходит сверху вниз по очереди.  Декораторы все еще являются экспериментальной функцией TypeScript. По этому, для их использования, вам нужно добавить в ваш tsconfig.json следующую настройку: Функция-декоратор вызывается компилятором, и компилятор сам подставляет в нее нужные аргументы. Сигнатура этой функции для методов класса следующая: Где: target — объект, для которого будет применен декоратор key — имя метода класса, который декорируется descriptor — дескриптор метода класса. С помощью дескриптора, мы можем получить доступ к исходному методу объекта. С помощью шаблонной типизации вы можете исключить неверное использование декоратора. Например, вы можете ограничить применение декоратора методами класса, у которых первый аргумент всегда имеет строковый тип, определив тип дескриптора следующим образом: В наших примерах мы будем использовать фабрики декораторов. Фабрика декораторов — это функция которая возвращает вызываюмую декоратором во время выполнения функцию. При рассмотрения примеров ниже мы будем использовать 2 модели данных: Во всех функциях декораторах для дескриптора мы будем использовать тип PropertyDescriptor, который является эквивалентом TypedPropertyDescriptor<any>.  Добавим функцию-хелпер createDecorator, которая поможет нам сократить синтаксический сахар создания декораторов: Проект построен на React + TypeScript. Для отображения состояние приложения на экран используется замечательная библиотека Mobx. Ниже в примерах я опущу связанные с Mobx части кода, чтобы сфокусировать ваше внимание на проблематики и ее решения.  Полную рабочую версию кода, можете найти в этом репозитории. Сперва создадим класс AppStore, который будет содержать в себе все состояние нашего маленького приложения. Приложение будет состоять из двух списков — список пользователей и список товаров. Данные будут использоваться из сервиса dummyjson. В результате рендера страницы вызываются 2 запроса на сервер для загрузки списков. AppStore выглядит следующим образом: Изменение значения флагов usersLoading и productsLoading контролирует видимость индикаторов загрузки списка. Можно увидеть в приведенном кода выше, эта функциональность в метода повторяется. Попробуем воспользоваться декораторами, чтобы убрать это дублирование. Инкапсулируем все флаги загрузки в один объект, который будет лежать в свойстве loading нашего хранилища состояния. Для этого определим интерфейс и базовый класс (для повторного использования кода по управлению состоянием загрузки флагов): В случае, если у вас нет возможности использовать наследование, можете использовать интерфейс ILoadable и реализовать собственный метод setLoading. Теперь изолируем общую функциональность контроля состояния флагов в декоратор. Для этого создадим обобщенную фабрику создания декораторов loadable используя функцию хелпер createDecorator: Фабричная функция является обобщенной и принимает на вход ключи свойств объекта, который будет лежать в свойстве loading интерфейса ILoadable. Для корректного использования этого декоратора, нужно чтобы наш класс реализовал интерфейс ILoadable. Воспользуемся наследованием от класса Loadable, Который уже реализует этот интерфейс и перепишем наш код следующим образом: В качестве типа для объекта в свойстве loading мы передаем динамически вычисляемый тип typeof defaultLoading от состояния по умолчанию этого объекта — defaultLoading. Так же, присваиваем это состояние свойству loading. За счет этого, строковые ключи, которые мы передаем в декоратор loadable контролируется типизацией typescript. Как вы видите, методы loadUsers и loadProducts лучше читаются, а функциональность показа спиннеров инкапсулирована в отдельный модуль. Фабрика декораторов loadable и интерфейс ILoadable абстрагированы от конкретной реализации стора и могут использоваться в неограниченном количестве сторов в приложении. Если вдруг, по какой либо причине, в приведенном выше примере сервис dummyjson перестанет быть доступным, наше приложение упадет с ошибкой и пользователь об этом не узнает. Давайте исправим эту ситуацию В каждом методе, появляется блок try … catch …, где обработка ошибок происходит в блоке catch. Всплывает уведомление в правом нижнем углу с текстом ошибки. Воспользуемся силой декораторов и инкапсулируем эту обработку в отдельный модуль, сделав ее абстрактной: Фабричная функция принимает на вход необязательные параметры — кастомный заголовок и описание ошибки, которые будут выводиться в уведомлении. Если параметры не будут заполнены то будет использоваться заголовок по умолчанию и сообщение из поля message ошибки. Используем функцию errorHandle в нашем коде: Так просто мы добавили функциональность обработки ошибок, убрали дублирования кода и сам код методов остался простым и читаемым. Допустим, что нам нужно сообщить об успешной загрузки списков пользователей и продуктов. Без декораторов код выглядел бы следующим образом: Так же, инкапсулируем эту функциональность в отдельный модуль и сделаем ее абстрактной: Фабричная функция принимает на вход обязательный параметр сообщение в уведомлении и не обязательный парметр — описание сообщения. Перепишем код с использованием этой функции: Если вы собираете данные вашего приложения и потом проводите анализ, для его оптимизации и отслеживания ошибок, то вам необходимо добавлять логи в код. Рассмотрим пример логирования — вывод в консоль. Добавим логи в наш сервис без использования декоратора: Как видите, код в методах усложнился для восприятия и понимания. И здесь еще нет кода, который включает или отключает логи в зависимости от стейджа сборки или по каким ни будь другим критериям, что сделает код еще более сложным. Создадим универсальный декоратор для логирования. В этом декораторе мы определили точки для логирования, которые можно настраивать передав их типизированный массив в первый параметр фабрики декоратора. По умолчанию логируется все. Так же, функции setDefaultLogPoint можно переопределить точки логирования  по умолчанию. Применим эту фабрику в нашем коде: Такая инкапсуляция поможет гибко настраивать включение и отключение логирования в приложении. Как видите, мы добавили много функционала не изменения при этом код с бизнес логикой приложения. Области применения декораторов разнообразны и потенциал их велик. С их помощью можно легко решить целый спектр прикладных задач, сделать код элегантным и простым для чтения. Благодаря декораторам, мы можем инкапсулировать повторяющийся сквозной функционал в модули, и переиспользовать в других частях проекта или других проектах. Однако в неумелых руках такой мощный инструмент может и навредить, например, изменить прямое назначение метода. Но, на мой взгляд, это не является основанием не изучать и применять эту технологию в своих проектах. Декораторы станут отличным дополнением в ваших проектах как в ОО так и в функциональной парадигме.   Репозиторий с исходным кодом можно найти тут Ссылка на статью на английском тут