Декораторы в 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 |
| Валидация DTO | class-validator |
| Cross-cutting | лог, метрики, права |
Когда лучше без декораторов
| Ситуация | Альтернатива |
|---|---|
| Одна обёртка логирования | функция withLog(fn) |
| Валидация | Zod + safeParse — 6.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 в entry | import "reflect-metadata" первым |
any в DI параметрах | явные типы токенов |
| Бизнес-логика в декораторе | только инфраструктура |
| Копировать Nest без Nest | не та инфраструктура |
Практика
- Реализуйте
@LogCallи убедитесь, что бизнес-результат не изменился. - Прочитайте один контроллер Nest и сопоставьте с таблицей маршрутов без фреймворка.
- Сравните валидацию: decorator vs Zod на одном DTO.
- Проверьте
tsconfigфреймворка из шаблона. - Решите для своего pet-проекта: decorators да/нет и зафиксируйте в README.