
Seja bem-vindo ao fascinante mundo dos decorators em JavaScript e TypeScript! Neste post, vamos explorar o poder dessas ferramentas incríveis, desde adicionar funcionalidades extras a classes até personalizar o comportamento de métodos. Vamos mergulhar juntos nesse universo de possibilidades e desvendar como essas pequenas anotações podem transformar a maneira como estruturamos e organizamos nosso código. Vamos lá!

Decorator para Classe
O “Class Decorator” é constituído apenas de uma função que herda o constructor da classe alvo, e pode ou não retornar uma nova classe estendendo o constructor recebido como parâmetro.
Exemplo:
Nos dois exemplos abaixo, mostro como adicionar um novo método a classe utilizando a criação de uma nova classe herdando a original, ou com a manipulação usando prototype.
function meuDecorator<T extends {new(...args: any[]): {}}>(construtor: T) { | |
return class extends construtor { | |
meuMetodoAdicional() { | |
console.log("Este é um método adicional adicionado pelo decorator"); | |
} | |
} | |
} | |
function meuOutroDecorator<T extends {new(...args: any[]): {}}>(construtor: T) { | |
constructor.prototype.meuOutroMetodoAdicional = function() { | |
console.log("Este é um outro método adicional adicionado pelo decorator"); | |
} | |
} |
Como utilizar:
Para utilizar um decorator no Typescript, precisamos somente “chamar” o decorator logo antes do nome da classe, utilizando o padrão @NomeDoDecorator
. Este método é chamado de “Suggar decorator”, implementado pelo Typescript.
@meuDecorator | |
@meuOutroDecorator | |
class MinhaClasse { | |
constructor(private valor: string) {} | |
meuMetodo() { | |
console.log(`Valor: ${this.valor}`); | |
} | |
} |
Decorator para Método
Como o Class Decorator, este é chamado exatamente antes do método, também sendo utilizado o padrão @
NomeDoDecorator
Este decorator recebe 3 parâmetros em sua função de retorno, sendo eles:
- target – Representação da classe que contém o método Pode ser usado para acessar ou alterar a classe e/ou seus membros.
- propertyKey – Nome do método que esta sendo utilizado
- descriptor – Objeto com a descrição do método, utilizado para alterar o comportamento do método e/ou seus metadados.
Exemplo:
Neste exemplo, criamos um decorator para validação de Método HTTP, para limitar uma requisição em somente um método.
// Limitador de métodos para request | |
import { Request, Response } from "express"; | |
export function AllowedMethod(method: "POST" | "GET" | "PUT" | "PATCH" | "DELETE") { | |
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { | |
const originalMethod = descriptor.value; | |
descriptor.value = async function (req: Request, res: Response): Promise<void | Response> { | |
if (req.method !== method) { | |
return res.status(405).json({ ok: false, message: "Method not allowed" }); | |
} | |
return originalMethod.apply(this, [req, res]); | |
}; | |
return descriptor; | |
}; | |
} |
No exemplo, modificamos o comportamento do método, para ser feita uma validação de Método HTTP antes de executar o código original.
Ao verificar a função decorada, definimos uma constante com uma cópia do método original, e após isso modificamos o valor com uma nova função que retornará uma execução do original.
Neste exemplo somente fazemos uma verificação, porém como pode ser visto, temos acesso a todas as informações do método original, sendo possível fazer uma variedade de modificações em seu comportamento, incluindo validação e/ou modificação de seus parâmetros.
Uso do Method decorator:
No código abaixo, utilizamos o decorator logo antes do método, podendo passar parâmetros para o mesmo.
Na primeira classe, inserimos o decorator no método de execução para limitar a requisição para somente métodos POST, e no segundo, para métodos GET.
class Login { | |
constructor() {} | |
@AllowedMethod("POST") | |
async exec(req: Request, res: Response) { | |
try { | |
const { username, password } = req.body; | |
// handle login | |
return res.status(200).json({ ok: true, user: {} }); | |
} catch (error) { | |
console.log(`Error: `, error); | |
} | |
} | |
} | |
class GetUser { | |
constructor() {} | |
@AllowedMethod("GET") | |
async exec(req: Request, res: Response) { | |
try { | |
const { id } = req.body; | |
// handle get user | |
return res.status(200).json({ ok: true, user: {} }); | |
} catch (error) { | |
console.log(`Error: `, error); | |
} | |
} | |
} |
Decorator puro para funções
Os exemplos acima, o decorator usado é uma feature experimental do Typescript, e somente funcionam em Classes e seus derivados, porém, é possível criar decorators para funções normais de um modo um pouco diferente.
Importante saber que este método não pode ser usado com o padrão do typescript @NomeDaFuncao
, porém segue um padrão um pouco parecido com o “method decorator” onde também pode ser passado parâmetros.
Este tipo de decorator pode ter vários nomes, como, “pure decorator” ou “simple decorator” e diferente do “suggar decorator” implementado pelo Typescript, este é feito para funções nativas ou arrow functions. Parar criar um decorator puro utilizamos a estratégia de “high order function”, enviando a função a ser executada como parâmetro, assim, podemos executar o decorator antes de chamar a função callback.
// Exemplo de decorator puro para funções (controle de acesso) | |
const user = { | |
name: "John Covv", | |
email: "contato@johncovv.com", | |
roles: ['user'] | |
} | |
function guard(allowedRoles: Array<'admin' | 'user'>, callback: Function) { | |
if (!user || !user.roles || !user.roles.some((role) => allowedRoles.includes(role as any))) { | |
throw new Error('acesso negado'); | |
} | |
return callback(); | |
} | |
guard(['user'], () => console.log('acesso concedido')); | |
//esperado: log "acesso concedido" | |
guard(['admin'], () => console.log('acesso concedido')); | |
//esperado: disparar o erro "acesso negado" |
No exemplo acima criamos o decorator “guard” para gerenciamento de rotas, como primeiro parâmetro devemos definir os cargos que tem acesso a request, e em seguida definimos a função callback que deve ser executada caso o usuário tenha o acesso correto.
É isso aí, chegamos ao final! Espero que essas dicas de decorators tenham feito seu código brilhar um pouquinho mais.
Seja o primeiro a comentar