Acabei brincando com os decoradores e decidi documentar o que descobri para quem quiser tirar vantagem disso antes que a documentação seja publicada. Sinta-se à vontade para editar isso se houver algum erro.
Pontos Gerais
- Decoradores são chamados quando a classe é declarada - não quando um objeto é instanciado.
- Vários decoradores podem ser definidos na mesma classe / propriedade / método / parâmetro.
- Decoradores não são permitidos em construtores.
Um decorador válido deve ser:
- Atribuível a um dos tipos de Decorador (
ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecorator
).
- Retorne um valor (no caso de decoradores de classe e decorador de método) que é atribuível ao valor decorado.
Referência
Método / Decorador formal de acessador
Parâmetros de implementação:
target
: O protótipo da classe ( Object
).
propertyKey
: O nome do método ( string
| symbol
).
descriptor
: A TypedPropertyDescriptor
- Se você não estiver familiarizado com as chaves de um descritor, recomendo ler sobre isso nesta documentação em Object.defineProperty
(é o terceiro parâmetro).
Exemplo - sem argumentos
Usar:
class MyClass {
@log
myMethod(arg: string) {
return "Message -- " + arg;
}
}
Implementação:
function log(target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) {
const originalMethod = descriptor.value; // save a reference to the original method
// NOTE: Do not use arrow syntax here. Use a function expression in
// order to use the correct value of `this` in this method (see notes below)
descriptor.value = function(...args: any[]) {
// pre
console.log("The method args are: " + JSON.stringify(args));
// run and store result
const result = originalMethod.apply(this, args);
// post
console.log("The return value is: " + result);
// return the result of the original method (or modify it before returning)
return result;
};
return descriptor;
}
Entrada:
new MyClass().myMethod("testing");
Resultado:
O método args são: ["testing"]
O valor de retorno é: Mensagem - teste
Notas:
- Não use a sintaxe da seta ao definir o valor do descritor. O contexto de
this
não será da instância se você o fizer.
- É melhor modificar o descritor original do que substituir o atual, retornando um novo descritor. Isso permite que você use vários decoradores que editam o descritor sem substituir o que outro decorador fez. Isso permite que você use algo como
@enumerable(false)
e @log
ao mesmo tempo (Exemplo: Ruim vs Bom )
- Útil : O argumento de tipo de
TypedPropertyDescriptor
pode ser usado para restringir quais assinaturas de método ( Exemplo de Método ) ou assinantes de acessador ( Exemplo de Acessor ) no qual o decorador pode ser colocado.
Exemplo - Com argumentos (Decorator Factory)
Ao usar argumentos, você deve declarar uma função com os parâmetros do decorador e retornar uma função com a assinatura do exemplo sem argumentos.
class MyClass {
@enumerable(false)
get prop() {
return true;
}
}
function enumerable(isEnumerable: boolean) {
return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) => {
descriptor.enumerable = isEnumerable;
return descriptor;
};
}
Decorador de método estático
Semelhante a um decorador de métodos com algumas diferenças:
- Está
target
parâmetro é a própria função construtora e não o protótipo.
- O descritor é definido na função construtora e não no protótipo.
Class Decorator
@isTestable
class MyClass {}
Parâmetro de implementação:
target
: A classe em que o decorador é declarado ( TFunction extends Function
).
Exemplo de uso : usando a API de metadados para armazenar informações em uma classe.
Decorador de propriedades
class MyClass {
@serialize
name: string;
}
Parâmetros de implementação:
target
: O protótipo da classe (Object
).
propertyKey
: O nome da propriedade ( string
| symbol
).
Exemplo de uso : Criando um @serialize("serializedName")
decorador e adicionando o nome da propriedade a uma lista de propriedades para serializar.
Decorador de Parâmetros
class MyClass {
myMethod(@myDecorator myParameter: string) {}
}
Parâmetros de implementação:
target
: O protótipo da classe ( Function
- parece Function
que não funciona mais. Você deve usar any
ou Object
aqui agora para usar o decorador em qualquer classe. Ou especificar o (s) tipo (s) de classe ao qual você deseja restringir)
propertyKey
: O nome do método ( string
| symbol
).
parameterIndex
: O índice do parâmetro na lista de parâmetros da função ( number
).
Exemplo simples
Exemplo (s) Detalhado (s)
@Injectable
em um decorador, consulte #