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:

TYPESCRIPT
// 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:

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

TYPESCRIPT
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:

TYPESCRIPT
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

TYPESCRIPT
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

TYPESCRIPT
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"
▶ Experimente

Saída:

TEXT 📖 Somente leitura
// 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

TYPESCRIPT
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

TYPESCRIPT
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

TYPESCRIPT
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

TYPESCRIPT
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

TYPESCRIPT
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):

TYPESCRIPT
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

TYPESCRIPT
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:

TEXT 📖 Somente leitura
User1
Products2
TYPESCRIPT
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:

TEXT 📖 Somente leitura
User1
Products2

▶ Exemplo: Aviso de método obsoleto

Saída:

TEXT 📖 Somente leitura
User1
Products2
TYPESCRIPT
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:

TEXT 📖 Somente leitura
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 a method = 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

📝 Exercícios

  1. Exercício básico (Dificuldade ⭐): Escreva um decorador de método @readonly que 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.
  2. 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: Adicione console.warn à função wrapper do método.
  3. 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) e Container.resolve(TargetClass) resolverá e injetará automaticamente a dependência.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%