Что такое декораторы в TypeScript?

MiddleTypeScript · Frontend·Обновлено 12 июля 2026
Коротко
Декораторы — это специальный синтаксис в TypeScript (начинается с @), позволяющий добавлять метаданные и изменять поведение классов, методов, свойств и параметров в декларативном стиле. По сути, это функции высшего порядка, применяемые к целевому элементу во время выполнения.

Декораторы в TypeScript

Декораторы — это экспериментальная функция TypeScript (и будущий стандарт ECMAScript), которая позволяет аннотировать и изменять классы и их члены с помощью специального синтаксиса @expression.

Типы декораторов

TypeScript поддерживает пять видов декораторов:

  • Декораторы классов — применяются к конструктору класса
  • Декораторы методов — применяются к дескриптору метода
  • Декораторы свойств — применяются к свойству класса
  • Декораторы параметров — применяются к параметру метода
  • Декораторы геттеров/сеттеров — применяются к аксессорам

Как работают декораторы

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

// Декоратор класса
function Sealed(constructor: Function) {
  Object.seal(constructor);
  Object.seal(constructor.prototype);
}

@Sealed
class BankAccount {
  balance: number = 0;
}

Фабрики декораторов

Чтобы передавать аргументы в декоратор, используют фабрику — функцию, возвращающую декоратор:

function Log(prefix: string) {
  return function (target: any, key: string, descriptor: PropertyDescriptor) {
    const original = descriptor.value;
    descriptor.value = function (...args: any[]) {
      console.log(`[${prefix}] Вызов ${key} с аргументами:`, args);
      return original.apply(this, args);
    };
    return descriptor;
  };
}

class UserService {
  @Log('UserService')
  getUser(id: number) {
    return { id, name: 'Иван' };
  }
}

Декоратор свойства

function Required(target: any, key: string) {
  // Сохраняем метаданные о том, что поле обязательно
  const requiredFields: string[] = Reflect.getMetadata('required', target) || [];
  requiredFields.push(key);
  Reflect.defineMetadata('required', requiredFields, target);
}

class CreateUserDto {
  @Required
  name: string;

  @Required
  email: string;
}

Порядок применения

При нескольких декораторах они вычисляются сверху вниз, а применяются снизу вверх:

@First()   // вычисляется 1-м, применяется 2-м
@Second()  // вычисляется 2-м, применяется 1-м
class Example {}

Reflect Metadata

Для работы с метаданными (например, в NestJS) используется пакет reflect-metadata. Он позволяет хранить и читать произвольные данные о классах и их членах.

Практическое применение

Декораторы широко используются в:

  • NestJS@Controller(), @Injectable(), @Get()
  • TypeORM@Entity(), @Column(), @PrimaryGeneratedColumn()
  • Angular@Component(), @NgModule()
  • class-validator@IsEmail(), @IsNotEmpty()

Включение декораторов

В tsconfig.json необходимо установить:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

Декораторы стадии TC39 Stage 3

Существует новый стандарт декораторов (Stage 3), который отличается от экспериментальной версии TypeScript. TypeScript 5.0+ поддерживает оба варианта, однако большинство фреймворков пока используют экспериментальную версию.

Что хочет услышать интервьюер

Понимание того, что декоратор — это функция высшего порядка, применяемая к классу или его члену

Знание типов декораторов: классов, методов, свойств, параметров

Понимание концепции фабрики декораторов для передачи аргументов

Знакомство с реальными фреймворками, использующими декораторы (NestJS, TypeORM, Angular)

Понимание необходимости включить `experimentalDecorators` в tsconfig

Пример: Декоратор метода — логирование вызовов

function Log(target: any, key: string, descriptor: PropertyDescriptor) {
  const original = descriptor.value;

  descriptor.value = function (...args: any[]) {
    console.log(`Вызов метода ${key}, аргументы:`, args);
    const result = original.apply(this, args);
    console.log(`Результат метода ${key}:`, result);
    return result;
  };

  return descriptor;
}

class Calculator {
  @Log
  add(a: number, b: number): number {
    return a + b;
  }
}

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

Пример: Фабрика декораторов — декоратор класса с аргументом

function Component(options: { selector: string; template: string }) {
  return function (constructor: Function) {
    // Сохраняем мета-информацию на прототипе класса
    constructor.prototype.selector = options.selector;
    constructor.prototype.template = options.template;
  };
}

@Component({
  selector: 'app-button',
  template: '<button>Нажми меня</button>',
})
class ButtonComponent {
  onClick() {
    console.log('Кнопка нажата');
  }
}

const btn = new ButtonComponent() as any;
console.log(btn.selector); // 'app-button'

Пример: Декоратор свойства — пометка обязательных полей

// Простой вариант без reflect-metadata
const requiredMetaKey = Symbol('required');

function Required(target: any, propertyKey: string) {
  const existing: string[] = target[requiredMetaKey] || [];
  target[requiredMetaKey] = [...existing, propertyKey];
}

function validate(instance: any): boolean {
  const required: string[] = instance[requiredMetaKey] || [];
  return required.every((key) => instance[key] !== undefined && instance[key] !== '');
}

class CreateUserDto {
  @Required
  name: string = '';

  @Required
  email: string = '';

  age?: number;
}

const dto = new CreateUserDto();
dto.name = 'Иван';
console.log(validate(dto)); // false — email не заполнен
dto.email = 'ivan@example.com';
console.log(validate(dto)); // true

Типичные ошибки

Путают декораторы классов и методов — не понимают разницу в сигнатурах функций

Забывают про фабрику декораторов и пытаются передать аргументы напрямую без обёртки

Не знают, что декораторы применяются снизу вверх при наличии нескольких на одном элементе

Путают экспериментальные декораторы TypeScript и новый стандарт TC39 Stage 3

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

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

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

TypeScript с нуля

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

Feature-Sliced Design

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

Next.js - с нуля

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