Mr Josselin A - TypeScript









Décorateurs

Références

L'actualité

Librairie

L'information

Introduction

Avec l'introduction de Classes dans TypeScript et ES6, il existe désormais certains scénarios nécessitant des fonctionnalités supplémentaires pour prendre en charge l'annotation ou la modification de classes et de membres de classes. Les décorateurs permettent d'ajouter des annotations et une syntaxe de méta-programmation pour les déclarations de classe et les membres. Les décorateurs sont une proposition de phase 2 pour JavaScript et sont disponibles en tant que fonctionnalité expérimentale de TypeScript.

Remarque : Les décorateurs sont une fonctionnalité expérimentale susceptible de changer dans les prochaines versions.

Pour activer le support expérimental pour les décorateurs, vous devez activer l'option experimentalDecorators du compilateur sur la ligne de commande ou dans votre tsconfig.json :

Ligne de commande
tsconfig.json

Décorateurs

Un décorateur est un type spécial de déclaration pouvant être associé à une déclaration de classe, une méthode, un accesseur, une propriété ou un paramètre. Les décorateurs utilisent le formulaire @expression, où expression doit être évalué à une fonction qui sera appelée à l'exécution avec des informations sur la déclaration décorée.
Par exemple, étant donné le décorateur, @sealed nous pourrions écrire la fonction sealed comme suit :

Ramarque : Vous pouvez voir un exemple plus détaillé d'un décorateur dans Class Decorators ci-dessous.

Usines de décorateur 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.

Nous pouvons écrire une usine de décorateur de la manière suivante :

Ramarque : Vous pouvez voir un exemple plus détaillé d'une usine de décorateurs dans Method Decorators, ci-dessous.

Composition du décorateur Plusieurs décorateurs peuvent être appliqués à une déclaration, comme dans les exemples suivants :

Sur une seule ligne :

Sur plusieurs lignes :

Lorsque plusieurs décorateurs postulent à une seule déclaration, leur évaluation est similaire à la composition de fonctions en mathématiques. Dans ce modèle, lors de la composition des fonctions f et g, le composite résultant ( f ∘ g ) ( x ) est équivalent à f ( g ( x )).

En tant que tel, les étapes suivantes sont effectuées lors de l'évaluation de plusieurs décorateurs sur une seule déclaration dans TypeScript :

  • Les expressions de chaque décorateur sont évaluées de haut en bas.
  • Les résultats sont ensuite appelés en tant que fonctions de bas en haut.

Si nous utilisions des usines de décorateurs, nous pouvons observer cet ordre d'évaluation avec l'exemple suivant :

Ce qui imprimerait cette sortie sur la console :

Évaluation du décorateur

Il existe un ordre bien défini pour l'application des décorateurs à diverses déclarations à l'intérieur d'une classe:

  • Les décorateurs de paramètres, suivis des décorateurs de méthodes, d'accesseurs ou de propriétés sont appliqués à chaque membre d'instance.
  • Les décorateurs de paramètres, suivis des décorateurs de méthodes, d'accesseurs ou de propriétés sont appliqués à chaque membre statique.
  • Les décorateurs de paramètres sont appliqués au constructeur.
  • Les décorateurs de classe sont appliqués pour la classe.

Décorateurs de classe

Un décorateur de classe est déclaré juste avant une déclaration de classe. Le décorateur de classe est appliqué au constructeur de la classe et peut être utilisé pour observer, modifier ou remplacer une définition de classe. Un décorateur de classe ne peut pas être utilisé dans un fichier de déclaration ou dans tout autre contexte ambiant (tel qu'une classe declare).

L'expression du décorateur de classe sera appelée en tant que fonction au moment de l'exécution, le constructeur de la classe décorée étant son seul argument.

Si le décorateur de classe retourne une valeur, il remplacera la déclaration de classe par la fonction constructeur fournie.

Ramarque : Si vous choisissez de renvoyer une nouvelle fonction constructeur, vous devez veiller à maintenir le prototype d'origine. La logique qui applique les décorateurs à l'exécution ne le fera pas pour vous.

Voici un exemple de classe decorator ( @sealed ) appliqué à la classe Greeter :

Nous pouvons définir le décorateur @sealed en utilisant la déclaration de fonction suivante :

Quand @sealed est exécuté, il va sceller le constructeur et son prototype.

Ensuite, nous avons un exemple de la façon de remplacer le constructeur.

Décorateurs de méthodes

Un décorateur de méthode est déclaré juste avant une déclaration de méthode. Le décorateur est appliqué au descripteur de propriété de la méthode et peut être utilisé pour observer, modifier ou remplacer une définition de méthode. Un décorateur de méthode ne peut pas être utilisé dans un fichier de déclaration, dans une surcharge ou dans tout autre contexte ambiant (tel qu'une classe declare).

L'expression du décorateur de méthode sera appelée en tant que fonction au moment de l'exécution, avec les trois arguments suivants :

  • Soit la fonction constructeur de la classe pour un membre statique, soit le prototype de la classe pour un membre d'instance.
  • Le nom du membre
  • Le descripteur de propriété du membre.

Ramarque : Le descripteur de propriété sera undefined si votre cible de script est inférieure à ES5.

Si le décorateur de la méthode retourne une valeur, elle sera utilisée comme descripteur de propriété pour la méthode.

Ramarque : La valeur de retour est ignorée si votre cible de script est inférieure à ES5.

Voici un exemple de méthode decorator ( @enumerable ) appliquée à une méthode de la classe Greeter :

Nous pouvons définir le décorateur @enumerable en utilisant la déclaration de fonction suivante :

Le décorateur @enumerable(false) ici est une usine de décorateurs. Lorsque le décorateur @enumerable(false) est appelé, il modifie la propriété enumerable du descripteur de propriété.

Accesseurs Décorateurs

Un décorateur d'accesseur est déclaré juste avant une déclaration d'accesseur. Le décorateur d'accesseur est appliqué au descripteur de propriété pour l'accesseur et peut être utilisé pour observer, modifier ou remplacer les définitions d'un accesseur. Un décorateur d'accesseur ne peut pas être utilisé dans un fichier de déclaration ou dans tout autre contexte ambiant (tel qu'une classe declare).


Ramarque : TypeScript interdit la décoration de l'accesseur get et set du membre unique. Au lieu de cela, tous les décorateurs du membre doivent être appliqués au premier accesseur spécifié dans l'ordre des documents. En effet, les décorateurs s'appliquent à un descripteur de propriété, qui combine l'accesseur get et l'accesseur set, et non chaque déclaration séparément.


L'expression du décorateur d'accesseurs sera appelée en tant que fonction au moment de l'exécution, avec les trois arguments suivants :

  • Soit la fonction constructeur de la classe pour un membre statique, soit le prototype de la classe pour un membre d'instance.
  • Le nom du membre
  • Le descripteur de propriété du membre.

Ramarque : Le descripteur de propriété sera undefined si votre cible de script est inférieure à ES5.

Si le décorateur d'accesseurs renvoie une valeur, celle-ci sera utilisée comme descripteur de propriété pour le membre.

Ramarque : La valeur de retour est ignorée si votre cible de script est inférieure à ES5.

Voici un exemple d'accesseur decorator( @configurable ) appliqué à un membre de la classe Point :

Nous pouvons définir le décorateur @configurable en utilisant la déclaration de fonction suivante :

Décorateurs de propriété

Un décorateur de propriété est déclaré juste avant une déclaration de propriété. Un décorateur de propriété ne peut pas être utilisé dans un fichier de déclaration ou dans tout autre contexte ambiant (tel qu'une classe declare).

L'expression du décorateur de propriétés sera appelée en tant que fonction au moment de l'exécution, avec les deux arguments suivants :
  • Soit la fonction constructeur de la classe pour un membre statique, soit le prototype de la classe pour un membre d'instance.
  • Le nom du membre

Ramarque : Un descripteur de propriété n'est pas fourni en tant qu'argument à un décorateur de propriétés en raison de la façon dont les décorateurs de propriétés sont initialisés dans TypeScript. En effet, il n'existe actuellement aucun mécanisme permettant de décrire une propriété d'instance lors de la définition des membres d'un prototype, et aucun moyen d'observer ou de modifier l'initialiseur d'une propriété. La valeur de retour est également ignorée. En tant que tel, un décorateur de propriété ne peut être utilisé que pour constater qu'une propriété d'un nom spécifique a été déclarée pour une classe.

Nous pouvons utiliser ces informations pour enregistrer des métadonnées sur la propriété, comme dans l'exemple suivant :

Nous pouvons ensuite définir le décorateur @format et les fonctions getFormat en utilisant les déclarations de fonction suivantes :

Le décorateur @format("Hello, %s") ici est une usine de décorateurs. Quand @format("Hello, %s") est appelé, il ajoute une entrée de métadonnées pour la propriété à l'aide de la fonction Reflect.metadata de la bibliothèque reflect-metadata. Quand getFormat est appelé, il lit la valeur de métadonnées pour le format.


Ramarque : Cet exemple nécessite la bibliothèque reflect-metadata. Voir Métadonnées pour plus d'informations sur la bibliothèque reflect-metadata.

Paramètres Décorateurs

Un décorateur de paramètres est déclaré juste avant une déclaration de paramètre. Le paramètre décorator est appliqué à la fonction pour un constructeur de classe ou une déclaration de méthode. Un décorateur de paramètres ne peut pas être utilisé dans un fichier de déclaration, une surcharge ou tout autre contexte ambiant (tel qu'une classe declare).

L'expression du décorateur de paramètres sera appelée en tant que fonction au moment de l'exécution, avec les trois arguments suivants :

  • Soit la fonction constructeur de la classe pour un membre statique, soit le prototype de la classe pour un membre d'instance.
  • Le nom du membre
  • Index ordinal du paramètre dans la liste des paramètres de la fonction.

Ramarque : Un décorateur de paramètres ne peut être utilisé que pour constater qu'un paramètre a été déclaré sur une méthode.

La valeur renvoyée par le décorateur de paramètres est ignorée.

Voici un exemple de paramètre decorator ( @required ) appliqué au paramètre d'un membre de la classe Greeter :

Nous pouvons ensuite définir les décorateurs @required et en utilisant @validate les déclarations de fonction suivantes :

Le décorateur @required ajoute une entrée de métadonnées qui marque le paramètre comme requis. Le décorateur @validate encapsule ensuite la greetméthode existante dans une fonction qui valide les arguments avant d'appeler la méthode d'origine.

Ramarque : Cet exemple nécessite la bibliothèque reflect-metadata. Voir Métadonnées pour plus d'informations sur la bibliothèque reflect-metadata.

Métadonnées

Certains exemples utilisent la bibliothèque reflect-metadata qui ajoute un polyfill pour une API de métadonnées expérimentale. Cette bibliothèque ne fait pas encore partie de la norme ECMAScript (JavaScript). Cependant, une fois que les décorateurs auront été officiellement adoptés dans le cadre de la norme ECMAScript, ces extensions seront proposées pour adoption.

Vous pouvez installer cette bibliothèque via npm :

TypeScript inclut un support expérimental pour l'émission de certains types de métadonnées pour les déclarations avec décorateurs. Pour activer cette prise en charge expérimentale, vous devez définir l'option emitDecoratorMetadata du compilateur sur la ligne de commande ou dans votre tsconfig.json :

Ligne de commande :

tsconfig.json :

Lorsqu'elle est activée, tant que la bibliothèque reflect-metadata a été importée, des informations supplémentaires sur le type de conception sont exposées au moment de l'exécution.

Nous pouvons le voir en action dans l'exemple suivant :

Le compilateur TypeScript injectera des informations de type au moment de la conception à l'aide du décorateur @Reflect.metadata. Vous pourriez considérer cela comme l'équivalent du TypeScript suivant :


Ramarque : Les métadonnées de Decorator sont une fonctionnalité expérimentale et peuvent introduire des modifications radicales dans les versions futures.