Перейти к основному содержимому

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

Разработчику Архитектору

Дальше: Объекты и классы · Паттерны · Справочник — декораторы


Декораторы — синтаксис @annotation для классов, методов, полей и параметров. Они позволяют добавлять метаданные и обёртки декларативно. В экосистеме TS их знают по NestJS, TypeORM, class-validator — но они требуют настройки компилятора и дисциплины команды.

Маршрут: классыдекораторыTypeORM.

Полные таблицы — Справочник — декораторы. Обзор — Обзор TypeScript в JS.


Стандарт и версии

ЭтапСтатус
Legacy (experimental)experimentalDecorators + emitDecoratorMetadata
ECMAScript Stage 3новый синтаксис, useDefineForClassFields и др.
TS 5+постепенное выравнивание с ES decorators

Перед включением в проекте проверьте версию TypeScript и документацию фреймворка (Nest 10+, TypeORM 0.3+).

Минимальный tsconfig для legacy-стека:

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

Разбор:

  • emitDecoratorMetadata нужен для DI (reflect типов параметров конструктора) — увеличивает размер и связь с reflect-metadata.

Декоратор метода: логирование

function LogCall(
_target: object,
propertyKey: string,
descriptor: PropertyDescriptor,
): void {
const original = descriptor.value as (...args: unknown[]) => unknown;

descriptor.value = function (this: unknown, ...args: unknown[]) {
console.log(`[${propertyKey}]`, args);
return original.apply(this, args);
};
}

class UserService {
@LogCall
findById(id: string): string {
return `user:${id}`;
}
}

Разбор:

  • Декоратор меняет descriptor.value — обёртка вокруг метода.
  • Типы unknown в обёртке — безопаснее any для args.

Декоратор класса (метаданные)

function Controller(path: string) {
return function <T extends { new (...args: unknown[]): object }>(ctor: T) {
Reflect.defineMetadata?.("path", path, ctor);
return ctor;
};
}

@Controller("/users")
class UsersController {}

В Nest путь и HTTP-метод задаются @Controller(), @Get() — тот же принцип.


Где декораторы уместны

СценарийПримеры
DI и маршрутыNestJS
ORM-сущностиTypeORM — 26.md
Валидация DTOclass-validator
Cross-cuttingлог, метрики, права

Когда лучше без декораторов

СитуацияАльтернатива
Одна обёртка логированияфункция withLog(fn)
ВалидацияZod + safeParse6.md
Маршруты без Nestявная таблица routes
Команда не знает TS decoratorsявный код проще onboarding

Декораторы скрывают поток: сложнее отладка и понять, кто изменил метод.


Декораторы полей (TypeORM)

import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";

@Entity("users")
export class User {
@PrimaryGeneratedColumn("uuid")
id!: string;

@Column({ unique: true })
email!: string;
}

Разбор:

  • ! — definite assignment assertion (поле заполнит ORM позже).
  • Схема БД и класс связаны декларативно — 26.md.

Ограничения и риски

  • Порядок применения нескольких декораторов важен (снизу вверх / по спецификации).
  • Tree-shaking и статический анализ хуже, чем у plain functions.
  • Миграция legacy → ES decorators — плановая работа.

Частые ошибки

ОшибкаЧто делать
Включили decorators без reflect-metadata в entryimport "reflect-metadata" первым
any в DI параметрахявные типы токенов
Бизнес-логика в декораторетолько инфраструктура
Копировать Nest без Nestне та инфраструктура

Практика

  1. Реализуйте @LogCall и убедитесь, что бизнес-результат не изменился.
  2. Прочитайте один контроллер Nest и сопоставьте с таблицей маршрутов без фреймворка.
  3. Сравните валидацию: decorator vs Zod на одном DTO.
  4. Проверьте tsconfig фреймворка из шаблона.
  5. Решите для своего pet-проекта: decorators да/нет и зафиксируйте в README.

Смежные статьи