Come implementare un decoratore typescript?

TypeScript 1.5 ora ha decoratori .

Qualcuno potrebbe fornire un semplice esempio che dimostri il modo corretto di implementare un decoratore e descrivere quali sono gli argomenti nelle possibili firme decoratore valide?

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

Inoltre, ci sono delle considerazioni sulle migliori pratiche che dovrebbero essere tenute a mente durante l’implementazione di un decoratore?

Ho finito per scherzare con i decoratori e ho deciso di documentare ciò che ho capito per chiunque volesse approfittarne prima che esca qualsiasi documentazione. Sentiti libero di modificare questo se vedi errori.

Punti generali

  • I decoratori vengono chiamati quando viene dichiarata la class, non quando viene istanziato un object.
  • È ansible definire più decoratori sulla stessa class / proprietà / metodo / parametro.
  • I decoratori non sono ammessi sui costruttori.

Un decoratore valido dovrebbe essere:

  1. Assegnabile a uno dei tipi di Decorator ( ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecorator ).
  2. Restituisce un valore (nel caso di decoratori di class e decoratore di metodi) che è assegnabile al valore decorato.

Riferimento


Metodo / Accessorio decoratore convenzionale

Parametri di implementazione:

  • target : il prototipo della class ( Object ).
  • propertyKey : il nome del metodo ( string | symbol ).
  • descriptor : A TypedPropertyDescriptor – Se non Object.defineProperty le chiavi di un descrittore, ti consiglio di leggerne in questa documentazione su Object.defineProperty (è il terzo parametro).

Esempio: senza argomenti

Uso:

 class MyClass { @log myMethod(arg: string) { return "Message -- " + arg; } } 

Implementazione:

 function log(target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor) { 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; } 

Ingresso:

 new MyClass().myMethod("testing"); 

Produzione:

Gli argomenti del metodo sono: [“testing”]

Il valore di ritorno è: Messaggio – test

Gli appunti:

  • Non utilizzare la syntax della freccia quando si imposta il valore del descrittore. Il contesto di this non sarà l’istanza se lo fai.
  • È meglio modificare il descrittore originale piuttosto che sovrascrivere quello corrente restituendo un nuovo descrittore. Ciò consente di utilizzare più decoratori che modificano il descrittore senza sovrascrivere ciò che un altro decoratore ha fatto. In questo modo puoi usare qualcosa come @enumerable(false) e @log allo stesso tempo (Esempio: Bad vs Good )
  • Utile : l’argomento type di TypedPropertyDescriptor può essere utilizzato per limitare le firme dei metodi ( Esempio di metodo ) o le firme accessorie ( Esempio di Accessor ) che possono essere applicate al decoratore.

Esempio – Con argomenti (Decorator Factory)

Quando si usano gli argomenti, è necessario dichiarare una funzione con i parametri del decoratore, quindi restituire una funzione con la firma dell’esempio senza argomenti.

 class MyClass { @enumerable(false) get prop() { return true; } } function enumerable(isEnumerable: boolean) { return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor) => { descriptor.enumerable = isEnumerable; return descriptor; }; } 

Decoratore di metodi statici

Simile a un decoratore di metodi con alcune differenze:

  • Il parametro target è la funzione di costruzione stessa e non il prototipo.
  • Il descrittore è definito sulla funzione di costruzione e non sul prototipo.

Decoratore di class

 @isTestable class MyClass {} 

Parametro di implementazione:

  • target : la class su cui è dichiarato il decoratore ( TFunction extends Function ).

Esempio di utilizzo : utilizzo della metadata api per memorizzare informazioni su una class.


Decoratore di proprietà

 class MyClass { @serialize name: string; } 

Parametri di implementazione:

  • target : il prototipo della class ( Object ).
  • propertyKey : il nome della proprietà ( string | symbol ).

Esempio di utilizzo : creazione di un @serialize("serializedName") e aggiunta del nome della proprietà a un elenco di proprietà da serializzare.


Parameter Decorator

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

Parametri di implementazione:

  • target : Il prototipo della class ( Function -it sembra che la Function non funzioni più. Dovresti usare any Object o Object qui ora per usare il decoratore all’interno di qualsiasi class.Oppure specificare il tipo di class che vuoi limitare a)
  • propertyKey : il nome del metodo ( string | symbol ).
  • parameterIndex : l’indice del parametro nell’elenco dei parametri della funzione ( number ).

Semplice esempio

Esempio / i dettagliato / i

  • Memoize decorator – Metodo, Ottieni / Imposta esempio decoratore Accessor

Una cosa importante che non vedo nelle altre risposte:

Fabbrica di decorazioni

Se vogliamo personalizzare l’applicazione di un decoratore a una dichiarazione, possiamo scrivere una fabbrica di decorazioni. A Decorator Factory è semplicemente una funzione che restituisce l’espressione che verrà chiamata dal decoratore in fase di esecuzione.

 // 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 { ... } 

Controllare il capitolo Decoratori del manuale di TypeScript.

 class Foo { @consoleLogger Boo(name:string) { return "Hello, " + name } } 
  • target: prototipo della class nel caso precedente è “Foo”
  • propertyKey: nome del metodo chiamato, nel caso precedente “Boo”
  • descrittore: descrizione dell’object => contiene la proprietà value, che a sua volta è la funzione stessa: function (name) {return ‘Hello’ + name; }

È ansible implementare qualcosa che registri ogni chiamata alla console:

 function consoleLogger(target: Function, key:string, value:any) { return value: (...args: any[]) => { var a = args.map(a => JSON.stringify(a)).join(); var result = value.value.apply(this, args); var r = JSON.stringify(result); console.log('called method' + key + ' with args ' + a + ' returned result ' + r); return result; } }