TypeScript decorators são uma maneira prática e simples de adicionar funcionalidades extras a objetos sem modificar o código original. Eles podem ser aplicados a classes, métodos, propriedades e parâmetros, aprimorando anotações e metadados.

O que são TypeScript decorators e por que usá-los?

O princípio adotado por TypeScript decorators não é novo. Outras linguagens de programação oferecem recursos similares, como atributos em C#, Decorators em Python e anotações em Java. Decorators expandem a funcionalidade de um objeto sem modificar o código original. O TypeScript já tem utilizado essa abordagem há algum tempo. Embora a maioria dos navegadores ainda não suporte TypeScript decorators, vale a pena explorar essa abordagem e suas possibilidades

TypeScript decorators são usados para adicionar anotações e metadados adicionais a classes TypeScript e seus elementos. Eles são capazes de modificar não apenas classes, mas também métodos, propriedades, métodos de acesso e parâmetros. Esses últimos podem ser validados e seus valores recuperados, seu grande diferencial em relação ao equivalente JavaScript.

Sintaxe e funcionamento de TypeScript decorators

Ao adicionar decorators a um objeto TypeScript, você está tecnicamente invocando uma função TypeScript que pode ser executada sem alterar o código original. Isso aumenta a funcionalidade do código por mantê-lo limpo. A sintaxe básica de um TypeScript decorator é a seguinte:

@nomeDoDecorator
typescript

Essa função pode ser criada com dois ou três parâmetros. A sintaxe para a função com três parâmetros é:

function decoratorFunction(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log(`Decorating ${propertyKey} of class ${target.constructor.name}`);
}
 
class MyClass {
    @decoratorFunction
    myMethod() {}
}
typescript

Estes são os elementos do TypeScript decorators:

  • target: Refere-se ao objeto ao qual o decorator foi atribuído.
  • propertyKey: É a string que contém o nome da classe à qual o decorator foi atribuído, seja um método ou atributo.
  • descriptor: Contém informações sobre o objeto ao qual o decorator é aplicado. Entre os atributos estão value, writable, enumerable ou configurable.

Aqui está a sintaxe de um TypeScript decorator com dois parâmetros:

function decoratorFunction(target: any) {
    console.log(`Decorating ${target.name}`);
}
 
@decoratorFunction
class MyClass {}
typescript

Nesse caso, o decorator foi aplicado a uma classe.

Tipos de TypeScript decorators

Existem diferentes tipos de TypeScript decorators, cada um com suas características próprias:

  • Class decorators
  • Method decorators
  • Property decorators
  • Accessor decorators
  • Parameter decorators

Class decorators: TypeScript decorators para classes

Para modificar as características de uma classe, como seu construtor, métodos ou propriedades, TypeScript decorators são uma ótima opção. O primeiro parâmetro recebido é o construtor, assim que você “decora” a classe com uma função. Observe abaixo um exemplo de código. Ele contém uma classe com propriedades privadas e públicas:

class Cliente {
  private static userType: string = "Genérico";
  private _email: string;
 
  public nome: string;
  public endereco: string = "";
  public cidade: string = "";
  public pais: string = "";
 
  constructor(nome: string, email: string) {
    this.nome = nome;
    this._email = email;
  }
 
  static get tipoUsuario() { 
return Cliente.userType; 
}
 
  get email() {
    return this._email;
  }
 
  set email(novoEmail: string) {
    this._email = novoEmail;
  }
 
  enderecoCompleto(): string {
    return `${this.endereco}\n${this.cidade}\n${this.pais}`;
  }
}
 
const c = new Cliente("ExemploCliente", "nome@exemplo.com");
c.endereco = "Rua Exemplo 2";
c.cidade = "São Paulo";
typescript

No próximo passo, usamos um TypeScript decorator para adicionar mais funcionalidades sem modificar o código original. Adicionamos o decorator @frozen à classe “Cliente”, impedindo alterações nos objetos após sua criação. Em algumas propriedades, usamos @required para indicar obrigatoriedade e @enumerable para listagens. Também usamos @deprecated para sinalizar entradas obsoletas. Primeiramente, definimos os decorators:

function frozen(constructor: Function) {
    Object.freeze(constructor);
    Object.freeze(constructor.prototype);
}
 
function required(target: any, propertyKey: string) {
    // Lógica para Required Decorator
}
 
function enumerable(value: boolean) {
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        descriptor.enumerable = value;
    };
}
 
function deprecated(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.warn(`O método ${propertyKey} está obsoleto.`);
}
typescript

Após aplicarmos TypeScript decorators, o código final fica assim:

@frozen
class Cliente {
  private static userType: string = "Genérico";
 
  @required
  private _email: string;
 
  @required
  public nome: string;
 
  public endereco: string = "";
  public cidade: string = "";
  public pais: string = "";
 
  constructor(nome: string, email: string) {
    this.nome = nome;
    this._email = email;
  }
 
  @enumerable(false)
  get userType() {
    return Cliente.userType;
  }
 
  get email() {
    return this._email;
  }
 
  set email(novoEmail: string) {
    this._email = novoEmail;
  }
 
  @deprecated
  enderecoCompleto(): string {
    return `${this.endereco}\n${this.cidade}\n${this.pais}`;
  }
}
 
const c = new Cliente("ExemploCliente", "nome@exemplo.com");
c.endereco = "Rua Exemplo 2";
c.cidade = "São Paulo";
typescript

Method decorators: TypeScript decorators para métodos

Você pode aplicar TypeScript decorators a métodos, exceto em arquivos de declaração, sobrecargas ou na palavra-chave declare. No exemplo a seguir, usamos @enumerable como decorator para o método getNome na classe “Pessoa”:

const enumerable = (value: boolean) => {
  return (target: any, propertyKey: string, propertyDescriptor: PropertyDescriptor) => {
    propertyDescriptor.enumerable = value;
  }
}
 
class Pessoa {
  primeiroNome: string = "Julia";
  ultimoNome: string = "Silva";
 
  @enumerable(true)
  getNome() {
    return `${this.primeiroNome} ${this.ultimoNome}`;
  }
}
typescript

Property decorators: TypeScript decorators para propriedades

TypeScript decorators aplicados a propriedades de uma classe têm dois parâmetros: a função do construtor da classe e o nome da propriedade. No exemplo abaixo, o decorator é usado para exibir o nome de uma propriedade (neste caso, o nome do cliente):

const printPropertyName = (target: any, propertyName: string) => {
  console.log(propertyName);
};
 
class Cliente {
  @printPropertyName
  nome: string = "Julia";
}
typescript

Accessor decorators: TypeScript decorators para métodos de acesso

Accessor decorators funcionam de maneira semelhante a Property decorators, mas têm um terceiro parâmetro adicional. Em nosso exemplo, é o Property descriptor de um cliente. Quando aplicamos um Accessor decorator, ele altera o valor do Property descriptor. O código abaixo mostra como mudar o valor booleano de enumerable:

const enumerable = (value: boolean
 
) => { 
  return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => { 
    descriptor.enumerable = value; 
  }; 
};
typescript

Agora, aplicando o decorator, temos o seguinte código:

class Cliente { 
  primeiroNome: string = "Julia"; 
  ultimoNome: string = "Silva"; 
 
  @enumerable(true) 
  get nomeCompleto() { 
    return `${this.primeiroNome} ${this.ultimoNome}`; 
  } 
}
typescript

Parameter decorators: TypeScript decorators para parâmetros

Parameter Decorators no TypeScript têm três parâmetros: a função do construtor da classe, o nome do método e o índice do parâmetro. Embora o decorator não permita alterar o parâmetro diretamente, ele é útil para validação. Por exemplo, você pode verificar o índice do parâmetro com o seguinte código:

function logParameter(target: Object, propertyKey: string, parameterIndex: number) { 
    console.log(`Decorating parameter ${parameterIndex} of ${propertyKey}`); 
}
typescript

Aplicação do decorator de parâmetro:

class Exemplo { 
    testMethod(param0: any, @logParameter param1: any) {} 
}
typescript
Dica

Ideal para sites estáticos e outras aplicações: com o Deploy Now da IONOS, você se beneficia de uma configuração rápida, de um staging simples e de fluxos de trabalho perfeitamente ajustados. Encontre o modelo ideal para as suas necessidades.

Este artigo foi útil?
Ir para o menu principal