TypeScript: Decoradores do TypeScript
Última atualização: 2026-08-26
Os decoradores são uma sintaxe experimental — eles utilizam a sintaxe @decorator para adicionar metadados a classes, métodos e propriedades, ou para modificar seu comportamento. São amplamente utilizados em frameworks como o NestJS e o Angular.
1. Visão geral dos decoradores
(1) O que é um decorador?
Um decorador é uma função — ele recebe um alvo (classe, método ou propriedade) como argumento e pode modificar ou aprimorar o comportamento desse alvo:
// Decorator Functions
function log(target: any, key: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Call ${key},Parameters:${args}`);
return original.apply(this, args);
};
}
class Calculator {
@log
add(a: number, b: number): number {
return a + b;
}
}
let calc = new Calculator();
calc.add(1, 2);
// Output:Call add,Parameters:1,2
(2) Ativar o suporte a decoradores
Os decoradores são um recurso experimental e devem ser ativados em tsconfig.json:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
(3) Cinco tipos de decoradores
| Tipo | Alvo modificado | Parâmetros |
|---|---|---|
| Decorador de classe | Definição de classe | Construtor |
| Decorador de método | Método de classe | Alvo, Nome do método, Descritor |
| Decorador de propriedade | Propriedade da classe | Alvo, Nome da propriedade |
| Decorador de parâmetro | Parâmetro da função | Destino, nome do método, índice do parâmetro |
| Decorador de acessores | getter/setter | alvo, nome do acessor, descritor |
2. Decoradores de classe
Um decorador de classe recebe um construtor como argumento e pode modificar ou substituir a definição da classe:
(1) Uso básico
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class Config {
host: string = "localhost";
port: number = 3000;
}
// Config Has been sealed——New properties cannot be added
(2) Fábrica de Decoradores
Quando forem necessários parâmetros, use uma função de fábrica para retornar um decorador:
function className(prefix: string) {
return function (constructor: Function) {
constructor.prototype._displayName = `${prefix}_${constructor.name}`;
};
}
@className("App")
class UserService {}
// UserService.prototype._displayName = "App_UserService"
(3) Sobrescrevendo o construtor
function logged<T extends { new (...args: any[]): {} }>(constructor: T) {
return class extends constructor {
constructor(...args: any[]) {
console.log(`Create ${constructor.name},Parameters:${args}`);
super(...args);
}
};
}
@logged
class User {
constructor(public name: string, public age: number) {}
}
let user = new User("Charlie", 20);
// Output:Create User,Parameters:Charlie,20
▶ Exemplo: Decorador de registro — Coleta automática de classes
const registry: Map<string, any> = new Map();
function Register(name: string) {
return function <T extends { new (...args: any[]): {} }>(constructor: T) {
registry.set(name, constructor);
return constructor;
};
}
@register("user")
class UserService {
getUser(id: number) { return { id, name: "User" + id }; }
}
@register("product")
class ProductService {
getProduct(id: number) { return { id, title: "Products" + id }; }
}
// Get a Service by Name
function getService(name: string) {
let Service = registry.get(name);
if (!Service) throw new Error(`Unregistered Services:${name}`);
return new Service();
}
let user = (getService("user") as UserService).getUser(1);
let product = (getService("product") as ProductService).getProduct(2);
console.log(user.name); // "User1"
console.log(product.title); // "Products2"
Saída:
// Executed successfully
3. Decoradores de métodos
Os decoradores de métodos podem observar, modificar ou substituir definições de métodos:
(1) Uso básico
function enumerable(value: boolean) {
return function (target: any, key: string, descriptor: PropertyDescriptor) {
descriptor.enumerable = value;
};
}
class Person {
constructor(public name: string) {}
@enumerable(false)
getFullName(): string {
return this.name;
}
}
(2) Registro de execução do método
function measure(target: any, key: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = function (...args: any[]) {
const start = performance.now();
const result = original.apply(this, args);
const end = performance.now();
console.log(`${key} Execution Time:${(end - start).toFixed(2)}ms`);
return result;
};
return descriptor;
}
class DataProcessor {
@measure
processLargeArray(data: number[]): number {
return data.reduce((sum, n) => sum + n, 0);
}
}
let processor = new DataProcessor();
processor.processLargeArray(Array.from({ length: 1000000 }, (_, i) => i));
// Output:processLargeArray Execution Time:XX.XXms
(3) Método Anti-Jitter
function debounce(delay: number) {
return function (target: any, key: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
let timer: any;
descriptor.value = function (...args: any[]) {
clearTimeout(timer);
timer = setTimeout(() => original.apply(this, args), delay);
};
return descriptor;
};
}
class SearchBox {
@debounce(300)
search(query: string): void {
console.log(`Search:${query}`);
}
}
let box = new SearchBox();
box.search("T"); // 300ms Then, if there is no new input, execute it
box.search("Ty");
box.search("Typ");
box.search("TypeScript"); // This will be executed only once
4. Decoradores de propriedades e decoradores de parâmetros
(1) Decoradores de propriedades
function format(formatStr: string) {
return function (target: any, key: string) {
// Store formatting information in metadata
Reflect.defineMetadata("format", formatStr, target, key);
};
}
class User {
@format("YYYY-MM-DD")
birthday: string = "2000-01-15";
@format("HH:mm:ss")
loginTime: string = "09:30:00";
}
(2) Decoradores de parâmetros
function required(target: any, key: string, index: number) {
const existing: number[] = Reflect.getMetadata("required", target, key) || [];
existing.push(index);
Reflect.defineMetadata("required", existing, target, key);
}
class UserService {
createUser(@required name: string, @required email: string, age?: number) {
// Parameter validation is handled by the framework at runtime.
}
}
5. Combinação de decoradores
É possível usar vários decoradores ao mesmo tempo — eles são executados em ordem, de baixo para cima (os que estão mais próximos do alvo são executados primeiro):
function log1(target: any, key: string, descriptor: PropertyDescriptor) {
console.log("log1 Applications");
}
function log2(target: any, key: string, descriptor: PropertyDescriptor) {
console.log("log2 Applications");
}
class Example {
@log1 // The Second Application(Outer layer)
@log2 // The First Application(Inner layer,Approach Methods)
method() {}
}
// Output:log2 Applications → log1 Applications
(1) Padrões comuns de combinação
class ApiController {
@logged
@measure
@debounce(100)
async fetchData(url: string): Promise<any> {
// First debounce → then measure → then logged(Bottom-up application)
}
}
▶ Exemplo: Decorador de método readonly
Saída:
User1
Products2
function readonly(target: any, key: string, descriptor: PropertyDescriptor) {
descriptor.writable = false;
return descriptor;
}
class Config {
@readonly
getVersion(): string { return "1.0.0"; }
}
let cfg = new Config();
// cfg.getVersion = () => "2.0.0"; // TypeError: Cannot assign to read-only property
Saída:
User1
Products2
▶ Exemplo: Aviso de método obsoleto
Saída:
User1
Products2
function deprecated(message: string) {
return function (target: any, key: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = function (...args: any[]) {
console.warn(`${key} is deprecated: ${message}`);
return original.apply(this, args);
};
return descriptor;
};
}
class LegacyService {
@deprecated("Use fetchUsers() instead")
getUsers(): string[] { return ["Alice", "Bob"]; }
fetchUsers(): string[] { return ["Alice", "Bob"]; }
}
let svc = new LegacyService();
svc.getUsers(); // getUsers is deprecated: Use fetchUsers() instead
Saída:
User1
Products2
❓ Perguntas Frequentes
P: Os decoradores são um recurso estável? R: Atualmente, eles são um recurso experimental (proposta em Estágio 3). A partir do TypeScript 5.0, a nova sintaxe de decoradores do Estágio 3 é suportada (não sendo mais necessário
experimentalDecorators), mas a sintaxe antiga ainda está disponível. Frameworks como o NestJS e o Angular utilizam atualmente a sintaxe antiga. Recomenda-se seguir as diretrizes fornecidas pelo seu framework.
P: É possível usar decoradores com funções? R: Não. Os decoradores só podem ser usados com classes e membros de classes (métodos, propriedades e parâmetros). Funções comuns não suportam decoradores — essa é uma limitação de projeto da proposta do JavaScript. Se você precisar aprimorar uma função, use o padrão de função de ordem superior (wrapper).
P: Qual é o impacto dos decoradores no desempenho? R: Os decoradores são executados uma única vez quando a classe é definida (e não a cada chamada), portanto, a sobrecarga é mínima. Os decoradores de método que modificam a função envolvida acarretam uma leve sobrecarga a cada chamada (uma chamada de função adicional), mas isso geralmente é insignificante. Em cenários em que o desempenho é crucial, evite usar várias camadas de decoradores em caminhos de acesso frequente.
P: É possível obter o mesmo efeito sem usar decoradores? R: Sim. Decoradores são, essencialmente, sintaxe simplificada —
@log method()é equivalente amethod = log(method). Sem decoradores, é possível obter o mesmo resultado usando funções de ordem superior, o padrão mixin, bibliotecas de AOP e outros métodos. A vantagem dos decoradores é que eles são declarativos, intuitivos e resultam em um código mais limpo.
📖 Resumo
- Os decoradores utilizam a sintaxe
@decoratorpara adicionar metadados a classes e membros de classes ou para modificar seu comportamento - Cinco tipos de decoradores: classes, métodos, propriedades, parâmetros e acessadores — cada um com uma assinatura de parâmetro diferente
- A fábrica de decoradores retorna uma função decoradora — permitindo que o decorador aceite parâmetros
- Os decoradores de classe podem substituir construtores (retornando uma nova classe), enquanto os decoradores de método podem envolver métodos
- Vários decoradores são aplicados de baixo para cima (os mais próximos do alvo são executados primeiro)
- Os decoradores são um recurso experimental que requer a opção
experimentalDecorators
📝 Exercícios
- Exercício básico (Dificuldade ⭐): Escreva um decorador de método
@readonlyque torne o método não sobrescritível (writable: false). Aplique-o a um método em uma classe e, em seguida, tente sobrescrever esse método em uma subclasse e observe o resultado. - Problema avançado (Dificuldade ⭐⭐): Escreva uma fábrica de decoradores
@deprecated(message)que exiba um aviso de obsolescência quando o método decorado for chamado. Dica: Adicioneconsole.warnà função wrapper do método. - Desafio (Dificuldade: ⭐⭐⭐): Implemente uma injeção de dependência simples usando um decorador de classe — marque a classe de serviço com
@Injectable(), marque a propriedade de dependência com@Inject(Service)eContainer.resolve(TargetClass)resolverá e injetará automaticamente a dependência.