TypeScript 1.5 a maintenant décorateurs .
Quelqu'un pourrait-il donner un exemple simple montrant la bonne manière de mettre en œuvre un décorateur et décrire la signification des arguments dans les signatures de décorateur valides?
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;
De plus, y a-t-il des considérations relatives aux meilleures pratiques à prendre en compte lors de la mise en œuvre d'un décorateur?
J'ai fini par jouer avec les décorateurs et j'ai décidé de documenter ce que j'avais imaginé pour tous ceux qui veulent en profiter avant que toute documentation ne soit publiée. N'hésitez pas à éditer ceci si vous constatez des erreurs.
Un décorateur valide devrait être:
- Attribuable à l'un des types de décorateur (
ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecorator
).- Renvoie une valeur (dans le cas des décorateurs de classe et de méthode) pouvant être assignée à la valeur décorée.
Paramètres de mise en œuvre:
target
: Le prototype de la classe (Object
).propertyKey
: nom de la méthode (string
| symbol
).descriptor
: A TypedPropertyDescriptor
- Si vous ne connaissez pas bien les clés d'un descripteur, je vous recommanderais de le lire dans cette documentation sur Object.defineProperty
(c'est le troisième paramètre) .Utilisation:
class MyClass {
@log
myMethod(arg: string) {
return "Message -- " + arg;
}
}
La mise en oeuvre:
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;
}
Contribution:
new MyClass().myMethod("testing");
Sortie:
Les arguments de la méthode sont: ["testing"]
La valeur de retour est: Message - testing
Remarques:
this
ne sera pas l'instance si vous le faites.@enumerable(false)
et @log
en même temps (Exemple: Bad vs Good )TypedPropertyDescriptor
peut être utilisé pour limiter les signatures de méthode ( Exemple de méthode ) ou les signatures d'accesseur (- Exemple d'accesseur ) le décorateur peut être mis.Lorsque vous utilisez des arguments, vous devez déclarer une fonction avec les paramètres du décorateur, puis renvoyer une fonction avec la signature de l'exemple sans arguments.
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;
};
}
Semblable à un décorateur de méthode avec quelques différences:
target
est la fonction constructeur elle-même et non le prototype.@isTestable
class MyClass {}
Paramètre d'implémentation:
target
: La classe sur laquelle le décorateur est déclaré (TFunction extends Function
).Exemple d'utilisation : Utilisation de l'api de métadonnées pour stocker des informations sur une classe.
class MyClass {
@serialize
name: string;
}
Paramètres de mise en œuvre:
target
: Le prototype de la classe (Object
).propertyKey
: nom de la propriété (string
| symbol
).Exemple d'utilisation : Création d'un décorateur @serialize("serializedName")
et ajout du nom de la propriété à une liste de propriétés à sérialiser.
class MyClass {
myMethod(@myDecorator myParameter: string) {}
}
Paramètres de mise en œuvre:
target
: Le prototype de la classe (Function
— il semble que Function
ne fonctionne plus. Vous devez maintenant utiliser any
ou Object
afin d'utiliser le décorateur dans n'importe quelle classe. Ou spécifier le (s) type (s) de classe que vous souhaitez limiter à)propertyKey
: nom de la méthode (string
| symbol
).parameterIndex
: L'index du paramètre dans la liste des paramètres de la fonction (number
).Une chose importante que je ne vois pas dans les autres réponses:
Si nous voulons personnaliser la manière dont un décorateur est appliqué à une déclaration, nous pouvons écrire une usine de décorateur. Une usine de décorateurs est simplement une fonction qui renvoie l'expression qui sera appelée par le décorateur lors de l'exécution.
// 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 { ... }
Consultez le manuel TypeScript chapitre Décorateurs .
class Foo {
@consoleLogger
Boo(name:string) { return "Hello, " + name }
}
Vous pouvez implémenter quelque chose qui enregistre chaque appel sur la 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;
}
}