typescript - not - unable to resolve signature of property decorator when called as an expression.




Comment mettre en place un décorateur dactylographié? (2)

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 la documentation ne soit publiée. N'hésitez pas à éditer ceci si vous constatez des erreurs.

Points généraux

  • Les décorateurs sont appelés lorsque la classe est déclarée, et non lorsqu'un objet est instancié.
  • Plusieurs décorateurs peuvent être définis sur la même classe / propriété / méthode / paramètre.
  • Les décorateurs ne sont pas autorisés sur les constructeurs.

Un décorateur valide devrait être:

  1. Assignable à l'un des types de décorateur ( ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecorator ).
  2. Renvoie une valeur (dans le cas des décorateurs de classe et de méthode) pouvant être assignée à la valeur décorée.

Reference

Décorateur de méthode / formel

Paramètres de mise en œuvre:

  • target : Le prototype de la classe ( Object ).
  • propertyKey : Le nom de la méthode ( string | symbol ).
  • descriptor : Un TypedPropertyDescriptor - Si vous ne connaissez pas bien les clés d'un descripteur, je vous conseillerais de le lire dans cette documentation sur Object.defineProperty (c'est le troisième paramètre).

Exemple - sans argument

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:

  • N'utilisez pas la syntaxe de flèche lors de la définition de la valeur du descripteur. Le contexte de this ne sera pas l'instance si vous faites.
  • Il est préférable de modifier le descripteur d'origine plutôt que de remplacer le descripteur actuel en renvoyant un nouveau descripteur. Cela vous permet d'utiliser plusieurs décorateurs qui modifient le descripteur sans remplacer ce qu'un autre décorateur a fait. Cela vous permet d'utiliser quelque chose comme @enumerable(false) et @log en même temps (Exemple: Bad vs Good )
  • Utile : L'argument type de TypedPropertyDescriptor peut être utilisé pour restreindre le type de signatures de méthode ( exemple de méthode ) ou d'accesseur ( exemple d' TypedPropertyDescriptor le décorateur peut être appliqué.

Exemple - Avec arguments (usine de décorateur)

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;
    };
}

Décorateur méthode statique

Semblable à un décorateur de méthode avec quelques différences:

  • Son paramètre target est la fonction constructeur elle-même et non le prototype.
  • Le descripteur est défini sur la fonction constructeur et non sur le prototype.

Décorateur de classe

@isTestable
class MyClass {}

Paramètre d'implémentation:

  • target : La classe sur laquelle le décorateur est déclaré (la fonction TFunction extends Function ).

Exemple d'utilisation : Utilisation de l'API de métadonnées pour stocker des informations sur une classe.

Décorateur de propriété

class MyClass {
    @serialize
    name: string;
}

Paramètres de mise en œuvre:

  • target : Le prototype de la classe ( Object ).
  • propertyKey : Le nom de la propriété ( string | symbol ).

Exemple d'utilisation : Création d'un @serialize("serializedName") et ajout du nom de la propriété à une liste de propriétés à sérialiser.

Paramètre Décorateur

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 ici pour pouvoir utiliser le décorateur dans n'importe quelle classe. Vous pouvez également spécifier le ou les types de classe que vous souhaitez limiter. à)
  • propertyKey : Le nom de la méthode ( string | symbol ).
  • parameterIndex : L'index du paramètre dans la liste des paramètres de la fonction ( number ).

Exemple simple

Exemple (s) détaillé (s)

TypeScript 1.5 a maintenant des decorators .

Quelqu'un pourrait-il donner un exemple simple montrant la bonne manière de mettre en œuvre un décorateur et décrivant 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 de meilleure pratique à prendre en compte lors de la mise en œuvre d'un décorateur?


Une chose importante que je ne vois pas dans les autres réponses:

Usine de décorateur

Si nous voulons personnaliser l'application d'un décorateur à 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 chapitre sur les décorateurs du manuel TypeScript.







decorator