TypeScript, un langage de programmation fortement typé, étend les capacités de JavaScript, offrant des outils plus robustes pour les projets de grande envergure. Il a été conçu pour pallier certaines des difficultés rencontrées lors de la programmation en JavaScript, notamment grâce à l’introduction des types.
Chaque élément de données dans un code TypeScript possède un type spécifique. Le compilateur TypeScript s’assure que chaque valeur respecte les règles associées à son type, détectant ainsi les erreurs potentielles avant même l’exécution du programme.
Cette approche, appelée vérification statique des types, permet de repérer les problèmes de développement en se basant sur les types des valeurs utilisées.
Outre une meilleure clarté et lisibilité du code, ainsi qu’une vérification statique des types, TypeScript offre des fonctionnalités supplémentaires qui favorisent la réutilisation et la maintenance du code. Les décorateurs en font partie.
Décorateurs TypeScript
Les décorateurs TypeScript sont une fonctionnalité permettant de modifier le comportement du code lors de son exécution, ou d’ajouter des métadonnées. Ils permettent la métaprogrammation, c’est-à-dire la capacité des programmes à traiter d’autres programmes comme des données et à modifier leur comportement.
En bref, les décorateurs sont des fonctions qui sont appelées pour exécuter une logique spécifique lorsque les éléments qu’ils décorent sont consultés ou modifiés, offrant ainsi une méthode pour ajouter des fonctionnalités supplémentaires.
Les décorateurs TypeScript peuvent être attachés aux définitions de classes, aux méthodes, aux propriétés, aux accesseurs (getters et setters) et aux paramètres de méthodes.
Dans TypeScript, les décorateurs sont précédés du symbole @ et se présentent sous la forme @expression, où l’expression est évaluée à une fonction appelée lors de l’exécution. Voici la syntaxe générale :
@nomDuDecorateur elementADecorer
Un exemple simple de décorateur de classe est présenté ci-dessous :
function journalisationClasse(cible: Function) { console.log("Le décorateur de journalisation de classe a été appelé"); console.log("Classe:", cible); } @journalisationClasse // @journalisationClasse est un décorateur class MaClasse { constructor() { console.log("Une instance de MaClasse a été créée"); } } const monInstance = new MaClasse();
L’exécution de ce code produit la sortie suivante :
Sortie :
Le décorateur de journalisation de classe a été appelé Classe: [class MaClasse] Une instance de MaClasse a été créée
La fonction `journalisationClasse()` prend un unique argument appelé `cible` de type `Function`. Cet argument représente le constructeur de la classe que nous décorons.
Pour utiliser `journalisationClasse()` comme décorateur, nous plaçons `@journalisationClasse` juste avant la définition de `MaClasse`. Le nom du décorateur doit correspondre à celui de la fonction que nous souhaitons utiliser pour décorer un élément.
Lors de la création d’une instance de `MaClasse`, la logique du décorateur est exécutée en plus du constructeur de la classe, comme le montre la sortie.
Les décorateurs sont encore considérés comme une fonctionnalité expérimentale dans TypeScript. Pour les utiliser, il faut activer l’option `experimentalDecorators` dans le fichier de configuration `tsconfig.json`.
Pour créer ce fichier, exécutez la commande suivante dans votre terminal :
tsc --init
Une fois le fichier `tsconfig.json` créé, ouvrez-le et décommentez la ligne `experimentalDecorators` comme indiqué ci-dessous :
De plus, assurez-vous que votre version cible de JavaScript est au moins ES2015.
Importance des décorateurs TypeScript
Un bon code se caractérise par sa lisibilité, sa réutilisabilité et sa maintenabilité. Un code lisible est facile à comprendre et à interpréter, communiquant clairement l’intention du développeur.
Un code réutilisable permet de réutiliser des composants spécifiques (fonctions, classes) dans d’autres parties d’une application ou dans une nouvelle application sans modifications majeures.
Un code maintenable peut être facilement modifié, mis à jour et corrigé tout au long de son cycle de vie.
Les décorateurs TypeScript permettent d’atteindre ces objectifs. Ils permettent d’améliorer le comportement du code à l’aide d’une syntaxe déclarative plus facile à lire. Vous pouvez encapsuler la logique dans des décorateurs et les invoquer en décorant différents éléments du code.
Les décorateurs ne sont pas à usage unique, ils sont par nature réutilisables. Vous pouvez définir un décorateur une seule fois et l’utiliser à plusieurs reprises. Vous pouvez les importer et les utiliser dans votre code pour modifier le comportement.
Cela évite la duplication de la logique, améliorant ainsi la réutilisabilité du code. Les décorateurs offrent également flexibilité et modularité, permettant de séparer différentes fonctionnalités en composants indépendants.
Ces avantages, combinés à l’écriture de code lisible et réutilisable, contribuent à une meilleure maintenabilité.
Types de décorateurs TypeScript
Les décorateurs peuvent être attachés aux classes, aux propriétés, aux méthodes, aux accesseurs et aux paramètres de méthodes. Cela définit les différents types de décorateurs :
#1. Décorateur de classe
Un décorateur de classe permet d’observer, modifier ou remplacer une définition de classe. Il est déclaré juste avant la classe. Il est appliqué au constructeur de la classe, qui est passé comme argument lors de son exécution.
Voici un exemple de décorateur de classe utilisé pour empêcher l’extension d’une classe :
function congelable(cible: Function) { Object.freeze(cible); Object.freeze(cible.prototype); } @congelable class Vehicule { roues: number = 4; constructor() { console.log("Un véhicule a été créé"); } } class Voiture extends Vehicule { constructor() { super(); console.log("Une voiture a été créée"); } } console.log(Object.isFrozen(Vehicule));
Pour empêcher l’extension d’une classe, nous utilisons `Object.freeze()`. Nous pouvons vérifier si la classe `Vehicule` est figée en utilisant `isFrozen()`, la sortie est la suivante :
true
#2. Décorateur de propriété
Un décorateur de propriété décore une propriété de classe, et il est déclaré juste avant cette propriété. Il peut être utilisé pour modifier ou observer une définition de propriété.
Lors de son exécution, il prend deux arguments : le constructeur de la classe (si la propriété est statique) ou le prototype de la classe (si la propriété est une instance), ainsi que le nom de la propriété.
En TypeScript, les membres statiques sont précédés du mot-clé `static` et sont accessibles sans instancier la classe. Les membres d’instance n’ont pas ce mot-clé et ne sont accessibles qu’après l’instanciation de la classe.
Voici un exemple de décorateur de propriété :
function decorateurRoues(cible: any, nomPropriete: string) { console.log(nomPropriete.toUpperCase()); } class Vehicule { @decorateurRoues roues: number = 4; constructor() { console.log("Un véhicule a été créé"); } }
L’exécution du code affiche :
ROUES
#3. Décorateur de méthode
Un décorateur de méthode est déclaré juste avant une définition de méthode. Il sert à observer, modifier ou remplacer une définition de méthode. Il prend trois arguments : le constructeur de la classe ou le prototype (comme pour les décorateurs de propriété), le nom du membre, et un descripteur de propriété pour le membre.
Un descripteur de propriété est un objet associé aux propriétés d’un objet, fournissant des informations sur les attributs et le comportement de la propriété.
Les décorateurs de méthodes sont utiles lorsque vous souhaitez effectuer une action avant ou après l’appel d’une méthode, ou pour enregistrer des informations sur la méthode. Ils peuvent être utilisés pour informer qu’une méthode est obsolète.
Voici un exemple :
const journalisationObsolescence = (cible: any, nomMethode: string, descripteur: PropertyDescriptor) => { console.log(`${nomMethode} est obsolète`); console.log(descripteur); }; class Vehicule { roues: number = 4; constructor() { console.log("Un véhicule a été créé"); } @journalisationObsolescence refaireLePlein(): void { console.log("Votre véhicule est en train de faire le plein"); } }
Sortie :
refaireLePlein est obsolète { value: [Function: refaireLePlein], writable: true, enumerable: false, configurable: true }
#4. Décorateurs d’accesseurs
En TypeScript, il existe deux types d’accesseurs : `get` et `set`. Ils sont utilisés pour contrôler l’accès aux propriétés. Les décorateurs d’accesseurs sont utilisés pour décorer ces méthodes et sont déclarés juste avant leur définition. Ils fonctionnent comme les décorateurs de méthodes.
Voici un exemple :
const journalisationRoues = (cible: any, nomAcces: string, descripteur: PropertyDescriptor) => { console.log(`${nomAcces} utilisé pour obtenir le nombre de roues`); console.log(descripteur); }; class Vehicule { private roues: number = 4; constructor() { console.log("Un véhicule a été créé"); } @journalisationRoues get nombreRoues(): number { return this.roues; } }
Sortie :
nombreRoues utilisé pour obtenir le nombre de roues { get: [Function: get nombreRoues], set: undefined, enumerable: false, configurable: true }
Il est important de noter que les décorateurs ne peuvent pas être appliqués à plusieurs accesseurs `get/set` du même nom. Par exemple, si vous créez un setter `set nombreRoues`, vous ne pouvez pas utiliser le même décorateur dessus.
#5. Décorateurs de paramètres
Un décorateur de paramètre est utilisé pour observer qu’un paramètre a été déclaré dans une méthode. Il est déclaré avant la déclaration du paramètre. Il prend trois arguments : le constructeur de la classe ou le prototype, le nom du membre et l’index ordinal du paramètre dans la liste des paramètres.
Voici un exemple :
const journalisationPassager = (cible: Object, clePropriete: string, indexParametre: number) => { console.log(`Décorateur sur le paramètre d'index ${indexParametre} de ${clePropriete}`); }; class Vehicule { private roues: number = 4; constructor() { console.log("Un véhicule a été créé"); } prendrePassager(emplacement: string, nombrePassagers: string, @journalisationPassager chauffeur: string) { console.log(`${nombrePassagers} pris à ${emplacement} par ${chauffeur}`); } deposerPassager(chauffeur: string, @journalisationPassager emplacement: string, nombrePassagers: string) { console.log(`${nombrePassagers} déposé à ${emplacement} par ${chauffeur}`); } }
Sortie :
Décorateur sur le paramètre d'index 2 de prendrePassager Décorateur sur le paramètre d'index 1 de deposerPassager
Conclusion
Les décorateurs TypeScript améliorent la lisibilité du code et facilitent l’écriture de code modulaire et réutilisable. Ils contribuent également à la maintenabilité. Bien qu’ils soient encore expérimentaux, ils sont très utiles et méritent d’être explorés.
Vous pouvez également consulter notre article sur la conversion de chaînes en nombres en TypeScript.