Ty­peS­cript de­co­ra­tors são uma maneira prática e simples de adicionar fun­ci­o­na­li­da­des extras a objetos sem modificar o código original. Eles podem ser aplicados a classes, métodos, pro­pri­e­da­des e pa­râ­me­tros, apri­mo­rando anotações e metadados.

O que são Ty­peS­cript de­co­ra­tors e por que usá-los?

O princípio adotado por Ty­peS­cript de­co­ra­tors não é novo. Outras lin­gua­gens de pro­gra­ma­ção oferecem recursos similares, como atributos em C#, De­co­ra­tors em Python e anotações em Java. De­co­ra­tors expandem a fun­ci­o­na­li­dade de um objeto sem modificar o código original. O Ty­peS­cript já tem utilizado essa abordagem há algum tempo. Embora a maioria dos na­ve­ga­do­res ainda não suporte Ty­peS­cript de­co­ra­tors, vale a pena explorar essa abordagem e suas pos­si­bi­li­da­des

Ty­peS­cript de­co­ra­tors são usados para adicionar anotações e metadados adi­ci­o­nais a classes Ty­peS­cript e seus elementos. Eles são capazes de modificar não apenas classes, mas também métodos, pro­pri­e­da­des, métodos de acesso e pa­râ­me­tros. Esses últimos podem ser validados e seus valores re­cu­pe­ra­dos, seu grande di­fe­ren­cial em relação ao equi­va­lente Ja­vaS­cript.

Sintaxe e fun­ci­o­na­mento de Ty­peS­cript de­co­ra­tors

Ao adicionar de­co­ra­tors a um objeto Ty­peS­cript, você está tec­ni­ca­mente invocando uma função Ty­peS­cript que pode ser executada sem alterar o código original. Isso aumenta a fun­ci­o­na­li­dade do código por mantê-lo limpo. A sintaxe básica de um Ty­peS­cript decorator é a seguinte:

@nomeDoDecorator
ty­pes­cript

Essa função pode ser criada com dois ou três pa­râ­me­tros. A sintaxe para a função com três pa­râ­me­tros é:

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

Estes são os elementos do Ty­peS­cript de­co­ra­tors:

  • 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 in­for­ma­çõ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 Ty­peS­cript decorator com dois pa­râ­me­tros:

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

Nesse caso, o decorator foi aplicado a uma classe.

Tipos de Ty­peS­cript de­co­ra­tors

Existem di­fe­ren­tes tipos de Ty­peS­cript de­co­ra­tors, cada um com suas ca­rac­te­rís­ti­cas próprias:

  • Class de­co­ra­tors
  • Method de­co­ra­tors
  • Property de­co­ra­tors
  • Accessor de­co­ra­tors
  • Parameter de­co­ra­tors

Class de­co­ra­tors: Ty­peS­cript de­co­ra­tors para classes

Para modificar as ca­rac­te­rís­ti­cas de uma classe, como seu cons­tru­tor, métodos ou pro­pri­e­da­des, Ty­peS­cript de­co­ra­tors são uma ótima opção. O primeiro parâmetro recebido é o cons­tru­tor, assim que você “decora” a classe com uma função. Observe abaixo um exemplo de código. Ele contém uma classe com pro­pri­e­da­des 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";
ty­pes­cript

No próximo passo, usamos um Ty­peS­cript decorator para adicionar mais fun­ci­o­na­li­da­des sem modificar o código original. Adi­ci­o­na­mos o decorator @frozen à classe “Cliente”, impedindo al­te­ra­ções nos objetos após sua criação. Em algumas pro­pri­e­da­des, usamos @required para indicar obri­ga­to­ri­e­dade e @enumerable para listagens. Também usamos @deprecated para sinalizar entradas obsoletas. Pri­mei­ra­mente, definimos os de­co­ra­tors:

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.`);
}
ty­pes­cript

Após apli­car­mos Ty­peS­cript de­co­ra­tors, 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";
ty­pes­cript

Method de­co­ra­tors: Ty­peS­cript de­co­ra­tors para métodos

Você pode aplicar Ty­peS­cript de­co­ra­tors a métodos, exceto em arquivos de de­cla­ra­ção, so­bre­car­gas 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}`;
  }
}
ty­pes­cript

Property de­co­ra­tors: Ty­peS­cript de­co­ra­tors para pro­pri­e­da­des

Ty­peS­cript de­co­ra­tors aplicados a pro­pri­e­da­des de uma classe têm dois pa­râ­me­tros: a função do cons­tru­tor da classe e o nome da pro­pri­e­dade. No exemplo abaixo, o decorator é usado para exibir o nome de uma pro­pri­e­dade (neste caso, o nome do cliente):

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

Accessor de­co­ra­tors: Ty­peS­cript de­co­ra­tors para métodos de acesso

Accessor de­co­ra­tors funcionam de maneira se­me­lhante a Property de­co­ra­tors, mas têm um terceiro parâmetro adicional. Em nosso exemplo, é o Property des­crip­tor de um cliente. Quando aplicamos um Accessor decorator, ele altera o valor do Property des­crip­tor. 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; 
  }; 
};
ty­pes­cript

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}`; 
  } 
}
ty­pes­cript

Parameter de­co­ra­tors: Ty­peS­cript de­co­ra­tors para pa­râ­me­tros

Parameter De­co­ra­tors no Ty­peS­cript têm três pa­râ­me­tros: a função do cons­tru­tor da classe, o nome do método e o índice do parâmetro. Embora o decorator não permita alterar o parâmetro di­re­ta­mente, 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}`); 
}
ty­pes­cript

Aplicação do decorator de parâmetro:

class Exemplo { 
    testMethod(param0: any, @logParameter param1: any) {} 
}
ty­pes­cript
Dica

Ideal para sites estáticos e outras apli­ca­ções: com o Deploy Now da IONOS, você se beneficia de uma con­fi­gu­ra­ção rápida, de um staging simples e de fluxos de trabalho per­fei­ta­mente ajustados. Encontre o modelo ideal para as suas ne­ces­si­da­des.

Ir para o menu principal