← Назад к вопросам

Позволяет ли декоратор передавать что-либо

1.8 Middle🔥 122 комментариев
#JavaScript Core

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Краткий ответ: Да, позволяет

Да, декораторы в JavaScript/TypeScript позволяют передавать различные значения: параметры декоратору, данные из декоратора в декорируемый элемент (класс, метод, свойство), а также передавать управление между декораторами в цепочке. Это один из ключевых механизмов их гибкости.

Как декораторы передают данные

1. Передача аргументов в декоратор (декораторы с параметрами)

Декораторы могут принимать аргументы, что делает их фабриками (decorator factories). Это позволяет настраивать поведение декоратора.

// Декоратор с параметрами
function LogWithPrefix(prefix: string) {
  // Фабрика возвращает собственно декоратор
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    
    descriptor.value = function (...args: any[]) {
      console.log(`${prefix}: Вызван метод ${propertyKey} с аргументами`, args);
      return originalMethod.apply(this, args);
    };
  };
}

class Calculator {
  @LogWithPrefix('Калькулятор')
  add(a: number, b: number): number {
    return a + b;
  }
}

const calc = new Calculator();
calc.add(2, 3); // Выведет: "Калькулятор: Вызван метод add с аргументами [2, 3]"

2. Передача метаданных через декораторы

Декораторы могут добавлять метаданные к классам, методам или свойствам с помощью библиотеки reflect-metadata, которую затем можно извлечь в другом месте программы.

import 'reflect-metadata';

// Декоратор, который добавляет метаданные
function ApiEndpoint(url: string) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    // Сохраняем URL в метаданных метода
    Reflect.defineMetadata('apiEndpoint', url, target, propertyKey);
  };
}

class UserController {
  @ApiEndpoint('/api/users')
  getUsers() {
    // Логика метода
  }
}

// Извлечение метаданных в другом месте
const endpoint = Reflect.getMetadata('apiEndpoint', UserController.prototype, 'getUsers');
console.log(endpoint); // Выведет: "/api/users"

3. Передача данных между декораторами

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

function FirstDecorator() {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log('Первый декоратор выполнен');
    descriptor.value = function (...args: any[]) {
      console.log('Первый декоратор: перед вызовом метода');
      return 'результат из первого декоратора';
    };
  };
}

function SecondDecorator() {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log('Второй декоратор выполнен');
    const original = descriptor.value;
    descriptor.value = function (...args: any[]) {
      console.log('Второй декоратор: перед вызовом метода');
      const result = original.apply(this, args);
      return `${result} + обработка вторым декоратором`;
    };
  };
}

class Example {
  @FirstDecorator()
  @SecondDecorator()
  method() {
    return 'оригинальный метод';
  }
}

const ex = new Example();
console.log(ex.method());
// Выведет:
// "Первый декоратор: перед вызовом метода"
// "результат из первого декоратора + обработка вторым декоратором"

4. Передача данных через модификацию дескриптора

Декораторы могут передавать данные, модифицируя PropertyDescriptor методов или определяя новые свойства в прототипе класса.

function ReadOnly(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  // Делаем свойство доступным только для чтения
  descriptor.writable = false;
  descriptor.configurable = false;
  
  // Добавляем дополнительную информацию в дескриптор
  descriptor.isReadOnly = true;
}

class Config {
  @ReadOnly
  apiUrl = 'https://api.example.com';
}

const config = new Config();
// config.apiUrl = 'новый url'; // Ошибка: свойство только для чтения

5. Передача контекста через параметры декоратора класса

Декораторы классов получают конструктор класса, что позволяет добавлять статические свойства или методы, доступные всему классу.

function Plugin(name: string) {
  return function <T extends { new (...args: any[]): {} }>(constructor: T) {
    // Добавляем статическое свойство
    return class extends constructor {
      static pluginName = name;
      static version = '1.0.0';
    };
  };
}

@Plugin('Аналитика')
class AnalyticsService {
  static config = { enabled: true };
}

console.log(AnalyticsService.pluginName); // "Аналитика"
console.log(AnalyticsService.version);    // "1.0.0"

Практические примеры передачи данных

Пример валидации с передачей правил

// Декоратор для валидации свойств
function Validate(rule: (value: any) => boolean) {
  return function (target: any, propertyKey: string) {
    let value: any;
    
    const getter = () => value;
    const setter = (newValue: any) => {
      if (!rule(newValue)) {
        throw new Error(`Некорректное значение для ${propertyKey}: ${newValue}`);
      }
      value = newValue;
    };
    
    // Переопределяем геттер и сеттер
    Object.defineProperty(target, propertyKey, {
      get: getter,
      set: setter,
      enumerable: true,
      configurable: true
    });
  };
}

class User {
  @Validate((age) => age >= 0 && age <= 120)
  age: number = 0;
  
  @Validate((email) => email.includes('@'))
  email: string = '';
}

const user = new User();
user.age = 25;    // OK
user.email = 'test@example.com'; // OK
// user.age = -5; // Ошибка валидации

Итог

Декораторы предоставляют мощные механизмы передачи данных:

  • Параметры декораторам через фабрики декораторов
  • Метаданные через Reflection API
  • Модификацию поведения через PropertyDescriptor
  • Цепочки вызовов при множественных декораторах
  • Расширение классов через добавление статических свойств

Эта гибкость делает декораторы инструментом для реализации AOP (Aspect-Oriented Programming), инверсии управления, валидации, логирования и других cross-cutting concerns в современных JavaScript/TypeScript приложениях.