Decorators em TypeScript: Metadata, Reflection e uso com NestJS na Prática Já leu

O que são Decorators em TypeScript? Decorators são uma funcionalidade experimental do TypeScript que permite adicionar metadados e modificar comportamentos de classes, métodos, propriedades e parâmetros em tempo de execução. Eles funcionam como funções que envolvem declarações, permitindo reutilizar lógica de forma elegante e declarativa. Essa é uma feature poderosa, especialmente quando combinada com reflection, tornando possível criar frameworks sofisticados como o NestJS. Para usar decorators, você deve habilitar a flag no . Existem cinco tipos principais: class decorators, method decorators, accessor decorators, property decorators e parameter decorators. Cada um opera em um nível diferente da hierarquia do código, permitindo controle fino sobre metadados e modificações. Metadata e Reflection com reflect-metadata Reflection é a capacidade de um programa inspecionar sua própria estrutura em tempo de execução. A biblioteca estende o Object global com métodos que permitem armazenar e recuperar metadados arbitrários. Isso é essencial para frameworks que precisam entender tipos e comportamentos dinamicamente. Instale com . O decorator com captura

O que são Decorators em TypeScript?

Decorators são uma funcionalidade experimental do TypeScript que permite adicionar metadados e modificar comportamentos de classes, métodos, propriedades e parâmetros em tempo de execução. Eles funcionam como funções que envolvem declarações, permitindo reutilizar lógica de forma elegante e declarativa. Essa é uma feature poderosa, especialmente quando combinada com reflection, tornando possível criar frameworks sofisticados como o NestJS.

Para usar decorators, você deve habilitar a flag experimentalDecorators no tsconfig.json. Existem cinco tipos principais: class decorators, method decorators, accessor decorators, property decorators e parameter decorators. Cada um opera em um nível diferente da hierarquia do código, permitindo controle fino sobre metadados e modificações.

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "target": "ES2020",
    "lib": ["ES2020"]
  }
}

Metadata e Reflection com reflect-metadata

Reflection é a capacidade de um programa inspecionar sua própria estrutura em tempo de execução. A biblioteca reflect-metadata estende o Object global com métodos que permitem armazenar e recuperar metadados arbitrários. Isso é essencial para frameworks que precisam entender tipos e comportamentos dinamicamente. Instale com npm install reflect-metadata.

O decorator com emitDecoratorMetadata captura informações de tipo automaticamente. Você pode então usar Reflect.getMetadata() para recuperar essas informações. No exemplo abaixo, criamos um decorator que registra tipos de parâmetros:

import 'reflect-metadata';

function LogTypes(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const types = Reflect.getMetadata('design:paramtypes', target, propertyKey);
  const returnType = Reflect.getMetadata('design:returntype', target, propertyKey);

  console.log(`Método: ${propertyKey}`);
  console.log(`Tipos dos parâmetros:`, types);
  console.log(`Tipo de retorno:`, returnType);

  return descriptor;
}

class Usuario {
  @LogTypes
  saudar(nome: string, idade: number): string {
    return `Olá ${nome}, você tem ${idade} anos`;
  }
}

const usuario = new Usuario();
usuario.saudar('João', 30);
// Output: Método: saudar, Tipos: [String, Number], Tipo de retorno: String

Decorators Prático: Validação e Autorização

Decorators são especialmente úteis para validação e autorização em controladores. No mundo real, é comum validar dados de entrada ou verificar permissões antes de executar um método. Vamos criar decorators reutilizáveis que resolvem problemas comuns:

import 'reflect-metadata';

// Decorator para validar email
function ValidateEmail(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const método = descriptor.value;

  descriptor.value = function(...args: any[]) {
    const email = args[0];
    if (!email.includes('@')) {
      throw new Error('Email inválido');
    }
    return método.apply(this, args);
  };

  return descriptor;
}

// Decorator para verificar autenticação
function Autenticado(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const método = descriptor.value;

  descriptor.value = function(...args: any[]) {
    const usuarioLogado = (this as any).usuario;
    if (!usuarioLogado) {
      throw new Error('Usuário não autenticado');
    }
    return método.apply(this, args);
  };

  return descriptor;
}

class ServicoEmail {
  usuario: string | null = 'joao@example.com';

  @Autenticado
  @ValidateEmail
  enviarEmail(email: string, mensagem: string): void {
    console.log(`Email enviado para ${email}: ${mensagem}`);
  }
}

const servico = new ServicoEmail();
servico.enviarEmail('maria@example.com', 'Olá!');

Decorators no NestJS: Aplicação Real

NestJS é um framework Node.js que usa decorators extensivamente para criar aplicações modulares e escaláveis. Vamos criar um exemplo prático de um controlador com decorators de validação e autorização:

import { Controller, Get, Post, Body, Param, UseGuards } from '@nestjs/common';
import { IsEmail, IsString } from 'class-validator';

class CriarUsuarioDto {
  @IsString()
  nome: string;

  @IsEmail()
  email: string;
}

// Guard customizado com decorator
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';

@Injectable()
class AdminGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    return request.user?.role === 'admin';
  }
}

@Controller('usuarios')
export class UsuariosController {
  private usuarios: any[] = [];

  @Get(':id')
  obterUsuario(@Param('id') id: string) {
    return this.usuarios.find(u => u.id === id);
  }

  @Post()
  @UseGuards(AdminGuard)
  criarUsuario(@Body() dto: CriarUsuarioDto) {
    const novoUsuario = { id: Date.now().toString(), ...dto };
    this.usuarios.push(novoUsuario);
    return novoUsuario;
  }
}

NestJS fornece decorators para métodos HTTP (@Get, @Post), injeção de dependência (@Injectable), validação automática com class-validator, e guards para autorização. A biblioteca class-transformer complementa isso, permitindo transformar dados em instâncias de classes com validação integrada.

Conclusão

Decorators em TypeScript, combinados com reflection e metadata, são ferramentas poderosas para criar código limpo, reutilizável e auto-documentado. Eles permitem separar concerns como validação, autenticação e logging da lógica de negócio. No contexto do NestJS, tornam-se absolutamente essenciais para estruturar aplicações enterprise. O aprendizado de decorators é fundamental para dominar frameworks modernos e escrever código profissional em TypeScript. Pratique criando seus próprios decorators antes de usar os prontos, isso consolida o entendimento.

Referências


Artigos relacionados