логотип 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 иконка
    Курсы
    ГлавнаяКаталог курсовFrontendBackendFullstack
    Практика
    КарьераПроектыPurpleПлюс
    Материалы
    БлогБаза знаний
    Документы
    Договор офертаПолитика конфиденциальностиПроверка сертификатаМиграция курсовРеферальная программа
    Реквизиты
    ИП Ларичев Антон АндреевичИНН 773373765379contact@purpleschool.ru

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

  • Курсы
    Иконка слояПерейти в каталог курсов
    • 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
      • Продвинутый JavaScript
      • Telegraf.js Иконка курсаСкоро!
      • TypeScript с нуля
      • Node.js с нуля
      • Nest.js с нуля
    • Картинка группы Golang

      Golang


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

      C#


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

      Python


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

      PHP


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

      Общее


      • Основы разработки
      • 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
    • Картинка группы Flutter

      Flutter


      • Основы Flutter Иконка курсаСкоро!
    • Картинка группы 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 089

    Комментарии

    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