логотип PurpleSchool
  • Бесплатно
      Карта развитияОсновы разработкиCSS Flexbox
    • Новостные рассылкиИконка стрелки
    • База знанийИконка стрелки
    • Карьерные пути
      • Frontend React разработчик
      • Frontend Vue разработчик
      • Backend разработчик Node.js
      • Fullstack разработчик React / Node.js
      • Mobile разработчик React Native
      • Backend разработчик Golang
      • Devops инженер
    • О нас
      • Отзывы
      • Реферальная программа
      • О компании
      • Контакты
    • Иконка открытия меню
      • Сообщество
      • PurpleПлюс
      • AI тренажёр
      • Проекты
    логотип PurpleSchool
    ютуб иконка
    Telegram иконка
    VK иконка
    VK иконка
    Курсы
    ГлавнаяКаталог курсовFrontendBackendFullstack
    Практика
    КарьераПроектыPurpleПлюс
    Материалы
    БлогБаза знаний
    Документы
    Договор офертаПолитика конфиденциальностиПроверка сертификатаМиграция курсовРеферальная программа
    Реквизиты
    ИП Ларичев Антон АндреевичИНН 773373765379contact@purpleschool.ru

    PurpleSchool © 2020 -2026 Все права защищены

  • Курсы
    Иконка слояПерейти в каталог курсов
    • FrontendИконка стрелки
    • BackendИконка стрелки
    • DevOpsИконка стрелки
    • MobileИконка стрелки
    • ТестированиеИконка стрелки
    • Soft-skillsИконка стрелки
    • ДизайнИконка стрелки
    • Картинка группы Общее

      Общее


      • Основы разработки
      • Основы Git
      • HTML и CSS
      • CSS Flexbox
      • Основы JavaScript
      • Продвинутый JavaScript
      • TypeScript с нуля
      • Neovim
    • Картинка группы React

      React


      • React и Redux Toolkit
      • Zustand
      • Next.js - с нуля
      • Feature-Sliced Design
    • Картинка группы Vue.js

      Vue.js


      • Vue 3 и Pinia
      • Nuxt
      • Feature-Sliced Design
    • Картинка группы Angular

      Angular


      • Angular 19 Иконка курсаСкоро!
    • Картинка группы Node.js

      Node.js


      • Основы Git
      • Основы JavaScript
      • Telegraf.js Иконка курсаСкоро!
      • Продвинутый JavaScript
      • TypeScript с нуля
      • Node.js с нуля
      • Nest.js с нуля
    • Картинка группы Golang

      Golang


      • Основы Git
      • Основы Golang
      • Продвинутый Golang
      • Golang - Templ Fiber HTMX
    • Картинка группы C#

      C#


      • Основы C#
    • Картинка группы PHP

      PHP


      • Основы PHP Иконка курсаСкоро!
    • Картинка группы Python

      Python


      • Старт в Python
      • Основы Python
      • Продвинутый Python
      • FastAPI Иконка курсаСкоро!
    • Картинка группы Общее

      Общее


      • Основы разработки
      • Docker и Ansible
      • Kubernetes и Helm
      • Микросервисы
      • Neovim
    • Картинка группы Общее

      Общее


      • Основы разработки
      • Основы Git
      • Основы Linux
      • Bash скрипты
      • Docker и Ansible
      • Kubernetes и Helm
      • Микросервисы
      • Neovim
    • Картинка группы Общее

      Общее


      • Основы разработки
      • Основы Git
      • Neovim
    • Картинка группы React Native

      React Native


      • HTML и CSS
      • Основы JavaScript
      • Продвинутый JavaScript
      • TypeScript с нуля
      • React и Redux Toolkit
      • React Native и Expo Router
    • Картинка группы Swift

      Swift


      • Основы Swift и iOS
    • Картинка группы Общее

      Общее


      • Продвинутое тестирование Иконка курсаСкоро!
      • Основы тестирования ПО
    • Картинка группы Общее

      Общее


      • Собеседование
      • Современный Agile
    • Картинка группы Figma

      Figma


      • Основы дизайна
      • Продвинутый дизайн Иконка курсаСкоро!
  • логотип PurpleSchool
    • Сообщество
    • PurpleПлюс
    • AI тренажёр
    • Проекты
    Главная
    Сообщество
    Новые декораторы в TypeScript 5.0

    Новые декораторы в TypeScript 5.0

    Аватар автора Новые декораторы в TypeScript 5.0

    Антон Ларичев

    Иконка календаря17 марта 2023

    Введение

    Сегодня мы более детально поговорим о декораторах в TypeScript 5.0 и рассмотрим примеры их использования.

    Подготовка проекта

    Разберем настройку проекта, которая необходима для работы с декораторами:

    • Выполним инициализацию файла package.json с помощью команды npm init.
    • Используем команду tsc --init для инициализации файла tsconfig.json.
    • Затем установим TypeScript Beta в качестве зависимости:
      npm i -D typescript@beta
      TypeScript Beta установит в dev-зависимости TypeScript 5.0 Beta.

    Важно: если на момент прочтения уже вышла стабильная версия, вы можете использовать ее.

    Теперь нужно сделать сборку. Для этого переходим в tsconfig.json, находим параметр outDir и задаем его значение равным ./dist, чтобы все наши транспилированные файлы сохранялись внутри директории dist:

    "outDir": "./dist",
    

    В файле package.json создаем несколько скриптов:

    • Первый - это скрипт build, который отвечает за сборку приложения. Он будет использовать локально установленную версию TypeScript:
      json "build": "tsc",
    • Второй скрипт - это start, который запускает приложение:
      json "start": "node ./dist/app.js"

    Кроме того, нам нужно создать файл app.ts, в котором мы будем экспериментировать.

    Декоратор метода

    Создадим класс Demo, который мы будем декорировать. У него будет метод exec, который принимает число и просто выводит это число с помощью console.log().

    class Demo {
        exec(a: number) {
            console.log(a);
        }
    }
    

    Добавляем декоратор methodDec:

    class Demo {
    
        @methodDec
        exec(a: number) {
            console.log(a);
        }
    }
    

    Все декораторы - это просто функции, которые всегда принимают два аргумента: target, который мы декорируем, и context - это контекст нашего декоратора. При этом декораторы, теоретически, могут быть универсальными, использоваться одновременно как на методе, так и на классе.

    Для типизации добавим немного Generics: This - это ссылка на наш класс, Args - это массив аргументов типа any, а Return - это тип возвращаемого значения нашего метода:

    function methodDec<This, Args extends any[], Return>(
        target: (this: This, ...args: Args) => Return,
        context
    ) {
    
    }
    

    Для context у нас есть ClassMethodDecoratorContext - это контекст декоратора:

    function methodDec<This, Args extends any[], Return>(
        target: (this: This, ...args: Args) => Return,
        context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return> 
    )
    

    Сам декоратор метода возвращает функцию где мы может изменить поведение исходного метода:

    class Demo {
    
        @methodDec 
        exec(a: number) {
            console.log(a);
        }
    }
    
    function methodDec<This, Args extends any[], Return>(
        target: (this: This, ...args: Args) => Return,
        context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>
    ) {
         console.log('Init method');
         return function(this: This, ...args: Args): Return {
              const res = target.call(this, ...args);
             return res;
        }
    }
    
    const demo = new Demo();
    demo.exec(1);
    

    Чтобы проверить, что наш декоратор работает, необходимо ввести команды npm run build и npm start:

    Init method
    1
    

    Пример - Декоратор Max number

    Для примера реализуем декоратор Max, который позволяет ограничить максимальное значение, передаваемое в функцию. Например, мы хотим, чтобы метод не выполнялся с определенным значением a.

    Max - это функция, которая принимает число, больше которого аргумент передать нельзя:

    class Demo {
        @Max(10)
        exec(a: number) {
            console.log(a);
        }
    }
    
    function Max(num: number) {
        return function <This, Args extends any[], Return>(
            target: (this: This, ...args: Args) => Return,
            context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>
        ) {
              return function(this: This, ...args: Args): Return {
                    if (args[0] > num) {
                        throw new Error(`Значение больше ${num}`)
                    }
                   const res = target.call(this, ...args);
                  return res;
                }
        }
    } 
    
    const demo = new Demo();
    demo.exec(1);
    demo.exec(11);
    

    После выполнения в во втором вызове получаем ошибку, так как значение больше 10.

    При этом мы можем прописать совершенно любую логику, которая нам необходима, и обернуть функцию не только с одним, но и с несколькими аргументами, если это необходимо.

    Декоратор класса

    Декоратор класса у нас принимает target - это new (…args: Args) и возвращает наш This, а в качестве контекста он использует ClassDecoratorContext с описанием самого класса:

    @classDec
    class Demo {
        @Max(10)
        exec(a: number) {
            console.log(a);
        }
    }
    
    // код
    
    function classDec<This, Args extends any[]>(
        target: new (...args: Args) => This,
        context: ClassDecoratorContext<new (...args: Args) => This>
    ) {
         console.log('Class init');
    }
    
    const demo = new Demo();
    demo.exec(1);
    

    Одно из частых использований такого декоратора, которое можно увидеть в библиотеках – это Dependency Injection.

    Декоратор поля

    У декоратора поля есть следующие параметры: target, у которого значение будет undefined, и context, у которого будет ClassFieldDecoratorContext.

    Кстати, не путайте с предыдущими версиями декораторов, у которых нет приставки “Context”. В этом случае вы можете получить неправильную типизацию.

    Контекст принимает This и тип, в данном случае - string. В отличие от остальных, он может возвращать модифицированную функцию инициализации:

    @classDec
    class Demo {
        @fieldDec
        name: string = 'Test';
    
        @Max(10)
        exec(a: number) {
            console.log(a);
        }
    }
    
    // код
    
    function fieldDec<This>(
        target: undefined,
        context: ClassFieldDecoratorContext<This, string>
    ) {
            console.log('Field init');
            return function (value: string) {
                console.log('Field init function')
                return value;
          }
    }
    
    // код
    

    Мы можем модифицировать поведение только один раз при инициализации. Но на текущий момент не все функции декоратора доступны, поэтому в будущем будет возможно реализовать изменение поведение при get и set.

    Декоратор setter

    Очень похож на декоратор метода, но имеет другую типизацию ClassSetterDecoratorContext:

    @classDec
    class Demo {
        private _surname!: string;
    
        @fieldDec
        name: string = 'Test';
    
        @setDec
        set surname(value: string) {
            this._surname = value;
        }
    
        @Max(10)
        exec(a: number) {
            console.log(a);
        }
    }
    
    function setDec<This, Return>(
        target: (this: This, arg: any) => Return,
        context: ClassSetterDecoratorContext<This, (this: This, arg: any) => Return>
    ) {
         console.log('Init method');
         return function (this: This, arg: any): Return {
             const res = target.call(this, arg);
             return res;
         }
    }
    
    // код
    

    Пример - setter проверка на строку

    Сделаем декоратор @IsString:

    @classDec
    class Demo {
        private _surname!: string;
    
        @fieldDec
        name: string = 'Test';
    
        @IsString
        set surname(value: string) {
            this._surname = value;
        }
    
        @Max(10)
        exec(a: number) {
            console.log(a);
        }
    }
    
    // код
    
    function IsString<This, Return>(
        target: (this: This, arg: any) => Return,
        context: ClassSetterDecoratorContext<This, (this: This, arg: any) => Return>
    ) {
         console.log('Set method');
         return function (this: This, arg: any): Return {
             if (typeof arg !== 'string') {
                 throw new Error ('Не строка')
             }
             const res = target.call(this, arg);
             return res;
         }
    }
    
    // код
    
    const demo = new Demo();
    demo.exec(1);
    

    При этом декораторы могут стекаться друг на друга. Например, мы можем поставить один декоратор над другим, и тогда первый декоратор обернёт функцию, а второй декоратор обернёт результат выполнения первого декоратора. И это большое преимущество декораторов - мы можем комбинировать их для создания более сложной функциональности.

    Иконка глаза6 210

    Комментарии

    0

    Постройте личный план изучения Основы разработки до уровня Middle — бесплатно!

    Основы разработки — часть карты развития Frontend, Backend, Mobile

    • step100+ шагов развития
    • lessons30 бесплатных лекций
    • lessons300 бонусных рублей на счет

    Бесплатные лекции

    Лучшие курсы по теме

    изображение курса

    Основы Git

    Антон Ларичев
    AI-тренажеры
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.9
    3 999 ₽ 6 990 ₽
    Подробнее
    изображение курса

    HTML и CSS

    Антон Ларичев
    AI-тренажеры
    Практика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.9
    3 999 ₽ 6 990 ₽
    Подробнее
    изображение курса

    CSS Flexbox

    Антон Ларичев
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.9
    бесплатно
    Подробнее
    Иконка чипа0