Декораторы в TypeScript/JavaScript: применяются ли они только к методам?

Ответ

Нет, декораторы в TypeScript (и экспериментальные декораторы в JavaScript) могут применяться к нескольким типам объявлений. В моих Node.js проектах с TypeScript я использую декораторы для:

1. Декораторы классов — для регистрации в DI-контейнере, добавления метаданных:

import { Injectable, Singleton } from 'tsyringe';

@Injectable() // Декоратор класса
@Singleton()  // Можно применять несколько декораторов
class DatabaseService {
  constructor(private config: ConfigService) {}

  async connect() {
    return await mongoose.connect(this.config.databaseUrl);
  }
}

2. Декораторы методов — для логирования, кэширования, валидации:

import { Get, Controller, UseGuards } from '@nestjs/common';

@Controller('users')
class UsersController {
  @Get() // Декоратор метода HTTP
  @UseGuards(AuthGuard) // Декоратор middleware
  @CacheTTL(60) // Декоратор кэширования
  async getAllUsers() {
    return await this.userService.findAll();
  }
}

3. Декораторы свойств — для валидации, сериализации, внедрения зависимостей:

import { Expose, Type } from 'class-transformer';
import { IsEmail, IsString } from 'class-validator';

class UserDto {
  @Expose() // Декоратор свойства для class-transformer
  id: number;

  @IsString() // Валидация
  @IsEmail()  // Можно несколько декораторов
  email: string;

  @Type(() => Date) // Преобразование типа
  createdAt: Date;
}

4. Декораторы параметров — для внедрения зависимостей в параметры конструктора/методов:

import { Inject } from '@nestjs/common';

class UserService {
  constructor(
    @Inject('USER_MODEL') // Декоратор параметра
    private userModel: Model<User>,
    @Inject(ConfigService)
    private config: ConfigService
  ) {}
}

5. Декораторы аксессоров (геттеров/сеттеров):

class Product {
  private _price: number;

  @Min(0) // Декоратор аксессора
  get price() {
    return this._price;
  }

  set price(value: number) {
    this._price = value;
  }
}

В Node.js с TypeScript декораторы полностью поддерживаются с флагом experimentalDecorators: true в tsconfig.json. В чистом JavaScript (Node.js ≥ 14) можно использовать экспериментальные декораторы с флагом --experimental-decorators, но TypeScript предлагает более стабильную и богатую реализацию.

Практический пример из моего опыта — декоратор для логирования времени выполнения методов:

function LogExecutionTime() {
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    const originalMethod = descriptor.value;

    descriptor.value = async function (...args: any[]) {
      const start = Date.now();
      const result = await originalMethod.apply(this, args);
      const duration = Date.now() - start;

      console.log(`${propertyKey} executed in ${duration}ms`);
      return result;
    };

    return descriptor;
  };
}

class UserService {
  @LogExecutionTime()
  async findUsers() {
    // Длительная операция
    return await db.users.findMany();
  }
}

Ответ 18+ 🔞

Ёпта, ну ты спросил — я тебе сейчас такую простыню накатал, что хоть в рамку вешай! Декораторы в TypeScript — это ж не только для классов, блядь. Это как швейцарский нож для разработчика, только без лезвия, которое постоянно теряется.

Смотри, в моих Node.js проектах я эти декораторы юзаю на все случаи жизни, прям овердохуища применений. Вот тебе полный разбор, чтоб ты не бздел от незнания.

1. Декораторы классов — это как наклейка «Хрупкое» на коробке, только для кода. Регистрируешь в DI, метаданные цепляешь — красота!

import { Injectable, Singleton } from 'tsyringe';

@Injectable() // Вот она, магия, ёпта!
@Singleton()  // А можно и пачку навесить, как стикеров на чемодан
class DatabaseService {
  constructor(private config: ConfigService) {}

  async connect() {
    return await mongoose.connect(this.config.databaseUrl);
  }
}

2. Декораторы методов — тут вообще пиздопроебибна простор для фантазии. Логирование, кэш, валидация — всё в одну строку.

import { Get, Controller, UseGuards } from '@nestjs/common';

@Controller('users')
class UsersController {
  @Get() // Говорим: этот метод — для GET-запросов
  @UseGuards(AuthGuard) // И чтоб не лез всякий **пидарас шерстяной**
  @CacheTTL(60) // И чтоб в кэше болтался минуту
  async getAllUsers() {
    return await this.userService.findAll();
  }
}

3. Декораторы свойств — моя любимая тема. Сделал DTO, навесил валидацию — и можно спать спокойно, доверия ебать ноль к входящим данным.

import { Expose, Type } from 'class-transformer';
import { IsEmail, IsString } from 'class-validator';

class UserDto {
  @Expose() // Это свойство наружу показываем
  id: number;

  @IsString() // Проверяем, что строка
  @IsEmail()  // И что почта, а не **манда с ушами**
  email: string;

  @Type(() => Date) // Автоматом в дату преобразуем
  createdAt: Date;
}

4. Декораторы параметровхитрая жопа, но удобная. Прямо в аргументы конструктора зависимости засунуть.

import { Inject } from '@nestjs/common';

class UserService {
  constructor(
    @Inject('USER_MODEL') // Говорим: дай сюда модель юзеров
    private userModel: Model<User>,
    @Inject(ConfigService) // А сюда — конфиг
    private config: ConfigService
  ) {}
}

5. Декораторы аксессоров — для геттеров и сеттеров. Чтоб сеттер не записал какую-нибудь залупу конскую.

class Product {
  private _price: number;

  @Min(0) // Чтоб цена не ушла в минус, ёпта!
  get price() {
    return this._price;
  }

  set price(value: number) {
    this._price = value;
  }
}

В Node.js с TypeScript это всё работает на ура, надо только в tsconfig.json флажок experimentalDecorators: true поставить. В чистом JS, конечно, терпения ноль ебать — там флаг экспериментальный, но TypeScript всё делает цивильно.

А вот тебе, кстати, реальный пример из жизни — декоратор, который засекает, сколько метод работает. Прям волнение ебать, когда видишь, какой метод тормозит!

function LogExecutionTime() {
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    const originalMethod = descriptor.value;

    descriptor.value = async function (...args: any[]) {
      const start = Date.now();
      const result = await originalMethod.apply(this, args);
      const duration = Date.now() - start;

      // Смотри, не **сам от себя охуел** от цифр
      console.log(`${propertyKey} executed in ${duration}ms`);
      return result;
    };

    return descriptor;
  };
}

class UserService {
  @LogExecutionTime() // Навесил — и профит!
  async findUsers() {
    // Какая-нибудь долгая операция с базой
    return await db.users.findMany();
  }
}

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