Decoradores em Typescript
Decoradores são uma ferramenta de programação declarativa que permitem adicionar metadados a classes e seus membros, modificando seu comportamento sem alterar o código original.
Os decoradores são funções que podem ser aplicadas a classes, métodos, acessores (getters e setters), propriedades e parâmetros.
Decoradores de Classe
Um decorador de classe é aplicado ao construtor da classe e permite modificar ou substituir a definição da classe.
O decorador de classe é uma função que recebe um parâmetro:
function classDecoratorFn(constructor: Function) {}O parâmetro é o construtor da classe. Por exemplo, vamos definir um decorador simples:
function sealed(constructor: Function) {
console.log("sealed decorator");
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class User {
name: string;
constructor(name: string) {
this.name = name;
}
print(): void {
console.log(this.name);
}
}O decorador sealed usa a função Object.seal para impedir a extensão do protótipo da classe User.
Para aplicar um decorador, utiliza-se o símbolo @. O decorador é colocado antes da definição da classe. Devido ao uso do decorador, por exemplo, não poderemos adicionar uma nova propriedade à classe User da seguinte forma:
Object.defineProperty(User, "age", {
value: 17,
});Além disso, os decoradores podem alterar o resultado do construtor. Nesse caso, a definição da função do decorador muda um pouco, mas ainda recebe o construtor da classe como parâmetro:
function logger<TFunction extends Function>(target: TFunction): TFunction {
let newConstructor: Function = function (name: string) {
console.log("Creating new instance");
this.name = name;
this.age = 23;
this.print = function (): void {
console.log(this.name, this.age);
};
};
return <TFunction>newConstructor;
}
@logger
class User {
name: string;
constructor(name: string) {
this.name = name;
}
print(): void {
console.log(this.name);
}
}
let tom = new User("Tom");
let bob = new User("Bob");
tom.print();
bob.print();Neste caso, o decorador logger é tipado com TFunction, que é uma extensão do tipo Function, ou seja, é o tipo de uma função construtora.
No próprio decorador, o construtor passado (target) não é utilizado. Em vez disso, um novo construtor é criado. Suponhamos que o construtor receba um parâmetro chamado name. O valor desse parâmetro é atribuído à propriedade this.name = name;. Além disso, o construtor define uma nova propriedade this.age e um método this.print(), que exibe no console os valores de ambas as propriedades.
Em seguida, o decorador é aplicado à classe User. Esta classe tem um construtor que define a propriedade name. No entanto, como substituímos o construtor, na realidade, ao criar um objeto User, serão definidas tanto a propriedade name quanto a propriedade age. Além disso, o método print será redefinido.
Saída no console do navegador:
Creating new instance Creating new instance Tom 23 Bob 23
É importante notar que substituir o construtor resulta na substituição completa de todas as propriedades e métodos da classe.