typescript - route - set page title in angular 6



Como implementar um decorador datilografado? (2)

Acabei brincando com os decoradores e decidi documentar o que descobri para quem quer tirar proveito disso antes de sair qualquer documentação. 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:

  1. Atribuível a um dos tipos de Decorador ( ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecorator ).
  2. Retorne um valor (no caso de decoradores de classe e decorador de método) que é atribuível ao valor decorado.

Reference

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 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: Bad vs Good )
  • Útil : O argumento de tipo de TypedPropertyDescriptor pode ser usado para restringir quais assinaturas de método ( Exemplo de Método ) ou TypedPropertyDescriptor de TypedPropertyDescriptor ( Exemplo de TypedPropertyDescriptor ) 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 do método estático

Semelhante a um decorador de métodos com algumas diferenças:

  • Seu parâmetro de target é 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") e adicionando o nome da propriedade a uma lista de propriedades a serem serializadas.

Decorador de Parâmetros

class MyClass {
    myMethod(@myDecorator myParameter: string) {}
}

Parâmetros de implementação:

  • target : O protótipo da classe ( Function - parece que Function não funciona mais. Você deve usar any ou Object aqui agora para usar o decorador em qualquer classe. Ou especifique o (s) tipo (s) de classe que deseja restringi-lo para)
  • 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)

https://code.i-harness.com

O TypeScript 1.5 agora possui decorators .

Alguém poderia fornecer um exemplo simples demonstrando a maneira correta de implementar um decorador e descrever o que significam os argumentos nas possíveis assinaturas válidas do decorador?

declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;
declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void;
declare type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void;
declare type ParameterDecorator = (target: Function, propertyKey: string | symbol, parameterIndex: number) => void;

Além disso, existem considerações sobre as melhores práticas que devem ser lembradas ao implementar um decorador?


Uma coisa importante que não vejo nas outras respostas:

Fábrica de decorador

Se quisermos personalizar como um decorador é aplicado a uma declaração, podemos escrever uma fábrica de decoradores. Uma Decorator Factory é simplesmente uma função que retorna a expressão que será chamada pelo decorador em tempo de execução.

// This is a factory, returns one of ClassDecorator,
// PropertyDecorator, MethodDecorator, ParameterDecorator
function Entity(discriminator: string):  {
    return function(target) {
        // this is the decorator, in this case ClassDecorator.
    }
}

@Entity("cust")
export class MyCustomer { ... }

Consulte o capítulo Decoradores do manual do TypeScript.





decorator