Avez-vous une question? (216) 97 656 803 hajjriadh@gmail.com
Angular

Présentation des décorateurs TypeScript

Un décorateur est une fonction placée avec @ devant une classe ou l’un de ses membres, qui lui ajoute un comportement sans modifier son code : journalisation, cache, validation ou enregistrement. Ce c...

0 chapitres 0 QCM Accès gratuit
Dans cet article

À propos de ce cours

Un décorateur est une fonction placée avec @ devant une classe ou l’un de ses membres, qui lui ajoute un comportement sans modifier son code : journalisation, cache, validation ou enregistrement. Ce chapitre montre comment écrire des décorateurs de méthode, de classe, de champ et d’accessor, et comment créer des fabriques de décorateurs qui reçoivent des paramètres. Vous utiliserez aussi l’objet context et apprendrez dans quel ordre s’exécutent plusieurs décorateurs. Vous découvrirez ensuite les décorateurs d’Angular (@Component, @Injectable…) et leurs remplaçants modernes (input(), inject()), ainsi que la différence entre décorateurs standard et expérimentaux. Cinq activités, dont la construction d’un mini-Angular, complètent la leçon.
  1. Objectifs

    • Comprendre ce qu'est un décorateur et à quoi il sert
    • Écrire des décorateurs de méthode, de classe, de champ et d'accesseur
    • Créer une fabrique de décorateurs qui reçoit des paramètres
    • Utiliser l'objet context et addInitializer
    • Prévoir l'ordre d'exécution de plusieurs décorateurs
    • Reconnaître les décorateurs d'Angular (@Component, @Injectable…) et leurs équivalents modernes
    • Distinguer décorateurs standard et décorateurs expérimentaux (experimentalDecorators)
  2. Présentation

    • Un décorateur est une fonction que l'on place devant une classe ou l'un de ses membres, précédée du symbole @. Elle reçoit l'élément décoré et peut l'observer, le modifier ou le remplacer.
    • Les décorateurs servent à ajouter un comportement transversal sans réécrire le code de chaque méthode : journalisation, mesure de performance, cache, validation, contrôle d'accès, enregistrement dans un catalogue…
    • Ils sont surtout connus grâce à Angular: @Component transforme une simple classe en composant, @Injectable en service.
    • Deux systèmes existent :
      • les décorateurs standard, issus de la proposition officielle de JavaScript (TC39), disponibles sans option depuis TypeScript 5.0: c'est ce que ce chapitre utilise ;
      • les décorateurs expérimentaux, l'ancienne version propre à TypeScript, activée par "experimentalDecorators": true: c'est celle qu'Angular configure dans ses projets.

Le principe d'un décorateur

  • Attention : les navigateurs et Node.js ne savent pas encore exécuter les décorateurs. node fichier.ts échoue avec SyntaxError: Invalid or unexpected token: il faut compiler avec tsc, qui transforme les décorateurs en JavaScript ordinaire, puis exécuter le fichier de dist.
  • Un premier décorateur de méthode

    • Un décorateur de méthode est une fonction qui reçoit deux arguments:
      • la méthode d'origine ;
      • un objet context qui la décrit (son nom, son genre, si elle est statique ou privée…).
    • S'il renvoie une fonction, celle-ci remplace la méthode. On appelle en général la méthode d'origine à l'intérieur, avec methode.call(this, ...args), pour conserver le bon this.
    function journaliser<This, Args extends unknown[], Retour>(
      methode: (this: This, ...args: Args) => Retour,
      context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Retour>,
    ) {
      const nom = String(context.name);
      return function (this: This, ...args: Args): Retour {
        console.log(`→ ${nom}(${args.join(', ')})`);
        const resultat = methode.call(this, ...args);
        console.log(`← ${nom} = ${resultat}`);
        return resultat;
      };
    }
    
    class Calculatrice {
      @journaliser
      ajouter(a: number, b: number): number {
        return a + b;
      }
    }
    
    console.log(new Calculatrice().ajouter(2, 3));
    // → ajouter(2, 3)
    // ← ajouter = 5
    // 5
    • Les paramètres génériques This, Args et Retour rendent le décorateur réutilisable sur n'importe quelle méthode, en conservant ses types. Si vous ne connaissez pas encore les génériques, retenez simplement ce modèle : il sera expliqué dans le chapitre consacré.
    • Le décorateur s'exécute une seule fois, au moment où la classe est définie, et non à chaque appel. C'est la fonction qu'il renvoie qui s'exécute à chaque appel.
    • Attention : la fonction renvoyée doit être une fonction classique (function), pas une fonction fléchée : une fonction fléchée ne reçoit pas son propre this et TypeScript le signale (error TS2683: 'this' implicitly has type 'any').
  • Où placer un décorateur ?

    • Un décorateur se place devant une classe ou l'un de ses membres: champ, méthode, getter, setter ou accessor. Devant une fonction isolée, TypeScript le refuse (error TS1206: Decorators are not valid here.).
    • Selon la cible, le décorateur reçoit et peut renvoyer des choses différentes :

    Les cibles des décorateurs TypeScript

    Cible Type de context Premier argument Valeur renvoyée (facultative)
    Classe ClassDecoratorContext la classe une classe qui la remplace
    Méthode ClassMethodDecoratorContext la méthode une nouvelle méthode
    Getter / setter ClassGetterDecoratorContext / ClassSetterDecoratorContext la fonction get ou set un nouveau get ou set
    Champ ClassFieldDecoratorContext undefined une fonction qui transforme la valeur initiale
    accessor ClassAccessorDecoratorContext { get, set } { get, set, init }
    1. Les décorateurs de classe

      • Un décorateur de classe reçoit la classe elle-même (son constructeur). Il peut l'enregistrer quelque part, ou renvoyer une nouvelle classe qui l'étend.
    // Décorateur de classe : enregistrer la classe dans un catalogue
    const registre = new Map<string, Function>();
    
    function enregistrer<T extends new (...args: any[]) => object>(classe: T, context: ClassDecoratorContext<T>) {
      registre.set(String(context.name), classe);
      console.log(`classe ${String(context.name)} enregistrée (kind = ${context.kind})`);
    }
    
    // Décorateur de classe qui renvoie une classe enrichie
    function horodater<T extends new (...args: any[]) => object>(classe: T, _context: ClassDecoratorContext<T>) {
      return class extends classe {
        creeLe = new Date(2026, 9, 29).toLocaleDateString('fr-FR');
      };
    }
    
    @enregistrer
    @horodater
    class Formation {
      constructor(public titre: string) {}
    }
    
    const f = new Formation('Angular');
    console.log(f, registre.size);
    // classe Formation enregistrée (kind = class)
    // Formation { titre: 'Angular', creeLe: '29/10/2026' } 1
    • La contrainte new (...args: any[]) => object signifie « n'importe quelle classe ». C'est l'un des rares endroits où any est nécessaire : TypeScript l'exige pour qu'une classe puisse en étendre une autre quels que soient les paramètres de son constructeur.
    • La propriété creeLe ajoutée par @horodater existe à l'exécution, mais TypeScript ne la connaît pas : f.creeLe provoquerait une erreur de compilation. Un décorateur ne modifie pas le type de la classe.
  • Les fabriques de décorateurs

    • Pour passer des paramètres à un décorateur, on écrit une fonction qui fabrique et renvoie le décorateur : c'est une fabrique de décorateurs (decorator factory). On l'utilise avec des parenthèses: @arrondir(2).
    • Tous les décorateurs d'Angular sont des fabriques : @Component({...}), @Injectable({...}), @Input()…

    Une fabrique de décorateurs

    function arrondir(decimales: number) {
      return function <This, Args extends unknown[]>(
        methode: (this: This, ...args: Args) => number,
        _context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => number>,
      ) {
        return function (this: This, ...args: Args): number {
          const facteur = 10 ** decimales;
          return Math.round(methode.call(this, ...args) * facteur) / facteur;
        };
      };
    }
    
    class Bulletin {
      constructor(private notes: number[]) {}
    
      @arrondir(2)
      moyenne(): number {
        return this.notes.reduce((s, n) => s + n, 0) / this.notes.length;
      }
    }
    
    console.log(new Bulletin([12, 15, 14]).moyenne());   // 13.67 au lieu de 13.666666666666666
    • Le décorateur ne s'applique qu'à des méthodes qui renvoient un number: sur une méthode qui renvoie une chaîne, TypeScript refuserait @arrondir(2). Le typage protège aussi les décorateurs.
  • Les décorateurs de champ et d'accesseur

    • Un décorateur de champ reçoit undefined (le champ n'existe pas encore) et peut renvoyer une fonction qui transforme la valeur initiale.
    • Pour intercepter aussi les modifications ultérieures, on déclare le champ avec le mot-clé accessor: TypeScript crée alors automatiquement un getter et un setter, que le décorateur peut remplacer. C'est la manière idéale de valider une valeur.
    // Décorateur de champ : transforme la valeur initiale
    function majuscules<This>(_valeur: undefined, _context: ClassFieldDecoratorContext<This, string>) {
      return (initiale: string) => initiale.toUpperCase();
    }
    
    class Groupe {
      @majuscules code = 'dev1';
    }
    console.log(new Groupe().code);   // DEV1
    
    // Décorateur d'accessor : valide chaque affectation
    function borne(min: number, max: number) {
      return function <This>(
        cible: ClassAccessorDecoratorTarget<This, number>,
        context: ClassAccessorDecoratorContext<This, number>,
      ): ClassAccessorDecoratorResult<This, number> {
        return {
          set(valeur: number) {
            if (valeur < min || valeur > max) {
              throw new RangeError(`${String(context.name)} doit être entre ${min} et ${max} (reçu ${valeur})`);
            }
            cible.set.call(this, valeur);
          },
        };
      };
    }
    
    class Evaluation {
      @borne(0, 20) accessor note = 10;
    }
    
    const e = new Evaluation();
    e.note = 15.5;
    console.log(e.note);   // 15.5
    try {
      e.note = 25;
    } catch (erreur) {
      if (erreur instanceof Error) console.log(`${erreur.name} : ${erreur.message}`);
    }
    // RangeError : note doit être entre 0 et 20 (reçu 25)
    • L'objet renvoyé peut aussi contenir init(valeur), pour vérifier ou transformer la valeur initiale, et get(), pour modifier la lecture.
  • L'objet context

    • Chaque décorateur reçoit un objet context qui décrit l'élément décoré :
      • kind: 'class', 'method', 'getter', 'setter', 'field' ou 'accessor';
      • name: le nom du membre (ou de la classe) ;
      • static et private: pour les membres ;
      • addInitializer(fn): enregistre une fonction exécutée à la création de chaque instance (ou à la définition de la classe pour un décorateur de classe ou un membre statique).
    function inspecter(_valeur: unknown, context: DecoratorContext) {
      const details = 'static' in context ? ` static=${context.static} private=${context.private}` : '';
      console.log(`kind=${context.kind} name=${String(context.name)}${details}`);
    }
    
    @inspecter
    class Stagiaire {
      @inspecter nom = 'Amira';
      @inspecter static effectif = 0;
      @inspecter accessor moyenne = 15.5;
      @inspecter #code = 'S-001';
      @inspecter presenter() { return this.nom; }
      @inspecter get initiale() { return this.nom[0]; }
    }
    
    // kind=accessor name=moyenne static=false private=false
    // kind=method name=presenter static=false private=false
    // kind=getter name=initiale static=false private=false
    // kind=field name=effectif static=true private=false
    // kind=field name=nom static=false private=false
    // kind=field name=#code static=false private=true
    // kind=class name=Stagiaire
    • Remarquez l'ordre : les méthodes et accesseurs sont décorés d'abord, puis les champs, et la classe en dernier, une fois tous ses membres prêts.
    • addInitializer permet par exemple de lier une méthode à son instance, pour qu'elle garde le bon this même détachée de son objet (cas typique d'un gestionnaire d'événement) :
    function lier<This extends object, Args extends unknown[], Retour>(
      methode: (this: This, ...args: Args) => Retour,
      context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Retour>,
    ) {
      context.addInitializer(function (this: This) {
        Object.defineProperty(this, context.name, { value: methode.bind(this) });
      });
    }
    
    class Bouton {
      constructor(public libelle: string) {}
    
      @lier
      cliquer(): string {
        return `Clic sur ${this.libelle}`;
      }
    }
    
    const { cliquer } = new Bouton('Valider');   // méthode détachée de son objet
    console.log(cliquer());                       // Clic sur Valider (sans @lier : TypeError)
    1. Composer plusieurs décorateurs

      • On peut empiler plusieurs décorateurs sur le même élément. Deux étapes, dans deux sens :
        • les expressions (les appels des fabriques) sont évaluées de haut en bas;
        • les décorateurs obtenus sont appliqués de bas en haut: le plus proche de la méthode l'enveloppe en premier.

    L'ordre d'exécution de plusieurs décorateurs

    function trace(nom: string) {
      console.log(`évaluation de @trace('${nom}')`);
      return function (_methode: unknown, _context: ClassMethodDecoratorContext) {
        console.log(`application de @trace('${nom}')`);
      };
    }
    
    class Ordre {
      @trace('A')
      @trace('B')
      methode() {}
    }
    
    // évaluation de @trace('A')
    // évaluation de @trace('B')
    // application de @trace('B')
    // application de @trace('A')
    • C'est l'équivalent de A(B(methode)). Dans l'exemple de la section « Les décorateurs de classe », @horodater est appliqué avant @enregistrer: c'est donc bien la classe enrichie qui est enregistrée.
  • Les décorateurs d'Angular

    • Angular utilise des décorateurs de classe pour indiquer le rôle de chaque classe. Ils portent des métadonnées (sélecteur, template, styles…) que le compilateur d'Angular lit pour générer le code de l'application.

    Les décorateurs d'Angular

    import { Component, HostListener, Injectable, Input, inject, input, signal } from '@angular/core';
    
    @Injectable({ providedIn: 'root' })
    export class PanierService {
      articles = signal<string[]>([]);
      ajouter(article: string): void {
        this.articles.update((liste) => [...liste, article]);
      }
    }
    
    // Style actuel : décorateur de classe + fonctions input(), inject() et propriété host
    @Component({
      selector: 'app-carte-formation',
      template: `
        <h3>{{ titre() }}</h3>
        <p>{{ prix() }} DT</p>
        <button (click)="reserver()">Réserver</button>
      `,
      styles: `h3 { color: #3178c6; }`,
      host: { '(window:keydown.escape)': 'annuler()' },
    })
    export class CarteFormation {
      private panier = inject(PanierService);
      titre = input.required<string>();
      prix = input(0);
    
      reserver(): void {
        this.panier.ajouter(this.titre());
      }
    
      annuler(): void {
        console.log('Réservation annulée');
      }
    }
    
    // Ancien style, que vous rencontrerez dans le code existant : décorateurs de membres
    @Component({ selector: 'app-ancienne-carte', template: `{{ titre }}` })
    export class AncienneCarte {
      @Input() titre = '';
    
      @HostListener('window:keydown.escape')
      annuler(): void {
        console.log('Réservation annulée');
      }
    }
    Décorateur Rôle Équivalent moderne
    @Component({...}) Déclare un composant : sélecteur, template, styles toujours utilisé
    @Injectable({...}) Déclare un service injectable toujours utilisé
    @Directive({...}) / @Pipe({...}) Déclare une directive / un pipe toujours utilisés
    @NgModule({...}) Déclare un module composants autonomes (standalone), par défaut
    @Input() / @Output() Entrée / sortie d'un composant input(), output(), model()
    @ViewChild() / @ContentChild() Accès à un élément du template viewChild(), contentChild()
    @HostListener() / @HostBinding() Événements et propriétés de l'élément hôte propriété host de @Component
    @Inject() dans le constructeur Injection de dépendance inject()
    • Les décorateurs de classe restent au cœur d'Angular. Les décorateurs de membres sont progressivement remplacés par des fonctions, mieux typées et compatibles avec les signals ; les deux styles fonctionnent et coexistent dans les projets existants.
  • Décorateurs standard et décorateurs expérimentaux

    • Avant d'être normalisés, les décorateurs existaient déjà dans TypeScript, dans une version différente et incompatible. Angular, NestJS ou TypeORM ont été construits sur cette ancienne version, et activent encore l'option "experimentalDecorators": true (vous la trouverez dans le tsconfig.json de tout projet Angular).

    Décorateurs standard et décorateurs expérimentaux

    // Ancienne syntaxe : "experimentalDecorators": true
    function journaliser(cible: object, cle: string, descripteur: PropertyDescriptor): PropertyDescriptor {
      const original = descripteur.value as (...args: unknown[]) => unknown;
      descripteur.value = function (this: unknown, ...args: unknown[]) {
        console.log(`→ ${cle}(${args.join(', ')})`);
        return original.apply(this, args);
      };
      return descripteur;
    }
    
    class Calculatrice {
      @journaliser
      ajouter(a: number, b: number): number {
        return a + b;
      }
    }
    • Sans l'option, TypeScript refuse ce décorateur, car le système standard l'appelle avec deux arguments et non trois :
    error TS1241: Unable to resolve signature of method decorator when called as an expression.
      The runtime will invoke the decorator with 2 arguments, but the decorator expects 3.
    • En pratique : pour utiliser les décorateurs d'Angular, la différence ne se voit pas. Mais si vous écrivez votre propre décorateur dans un projet Angular, il doit suivre la signature expérimentale (cible, cle, descripteur). Dans un projet sans experimentalDecorators, comme celui de ce cours, utilisez la signature standard.
  • Activité 1 : journaliser un compte bancaire

    • Dans le projet premiers-pas-typescript, créer src/16-compte.ts. Écrire le décorateur de méthode @journaliser qui affiche [LOG] nomMethode(arguments) avant d'appeler la méthode d'origine.
    • Créer la classe CompteBancaire (propriétés titulaire et solde), avec les méthodes deposer(montant) et retirer(montant) décorées, et afficher() non décorée. retirer renvoie false si le solde est insuffisant.
    const compte = new CompteBancaire('Amira');
    compte.deposer(500);
    compte.retirer(120.5);
    console.log(compte.retirer(1000));
    console.log(compte.afficher());
    
    // Résultat attendu (npx tsc puis node dist/16-compte.js) :
    // [LOG] deposer(500)
    // [LOG] retirer(120.5)
    // [LOG] retirer(1000)
    // false
    // Amira : 379.500 DT
    • Remplacer la fonction renvoyée par une fonction fléchée : quelle erreur obtenez-vous ? Puis essayer node src/16-compte.ts et noter le message.
  • Activité 2 : une fabrique @arrondir

    • Dans src/17-article.ts, écrire la fabrique arrondir(decimales: number) et l'appliquer à la classe Article (nom, prixHT) :
      • prixTTC(tva = 0.19), arrondi à 3 décimales ;
      • pointsFidelite() (prix HT divisé par 7), arrondi à l'entier.
    const clavier = new Article('Clavier', 45.333);
    console.log(clavier.prixTTC());
    console.log(clavier.prixTTC(0.07));
    console.log(clavier.pointsFidelite());
    
    // Résultat attendu :
    // 53.946
    // 48.506
    // 6
    • Écrire @arrondir sans parenthèses : quel code d'erreur obtenez-vous ? Puis décorer une méthode qui renvoie une chaîne : que se passe-t-il ?
  • Activité 3 : un cache avec @memoriser

    • Dans src/18-tarifs.ts, écrire un décorateur @memoriser pour les méthodes qui reçoivent un nombre: il garde les résultats dans une Map et, si la méthode est rappelée avec le même argument, renvoie le résultat déjà calculé en affichant nomMethode(argument) : depuis le cache.
    • Dans la classe Tarifs, la méthode tarifGroupe(personnes) calcule le prix d'un groupe (300 DT par personne pour les 5 premières, 250 DT au-delà) et incrémente le compteur calculs.
    const tarifs = new Tarifs();
    console.log(tarifs.tarifGroupe(8));
    console.log(tarifs.tarifGroupe(3));
    console.log(tarifs.tarifGroupe(8));
    console.log(`Calculs réels : ${tarifs.calculs}`);
    
    // Résultat attendu :
    // 2250
    // 900
    //   tarifGroupe(8) : depuis le cache
    // 2250
    // Calculs réels : 2
    • Créer une seconde instance new Tarifs() et appeler tarifGroupe(8): le résultat vient-il du cache ? Que vaut calculs pour cette instance ? Expliquer en commentaire où se trouve la Map et pourquoi elle est partagée.
  • Activité 4 : valider une inscription

    • Dans src/19-inscription.ts, écrire deux décorateurs d'accessor:
      • la fabrique @borne(min, max) qui refuse (RangeError) une valeur hors limites ou NaN, à l'initialisation (init) comme à l'affectation (set) ;
      • @nonVide qui refuse une chaîne vide et enregistre la valeur sans espaces superflus.
    • Les appliquer à la classe Inscription: nom (non vide), age (16 à 60), note (0 à 20).
    const ins = new Inscription();
    ins.nom = '  Amira Ben Ali ';
    ins.age = 22;
    ins.note = 15.5;
    console.log(`${ins.nom} | ${ins.age} ans | ${ins.note}/20`);
    
    for (const essai of [() => { ins.note = 21; }, () => { ins.age = 12; }, () => { ins.nom = '   '; }]) {
      try {
        essai();
      } catch (erreur) {
        if (erreur instanceof Error) console.log(`${erreur.name} : ${erreur.message}`);
      }
    }
    console.log(`${ins.nom} | ${ins.age} ans | ${ins.note}/20`);
    
    // Résultat attendu :
    // Amira Ben Ali | 22 ans | 15.5/20
    // RangeError : note doit être entre 0 et 20 (reçu 21)
    // RangeError : age doit être entre 16 et 60 (reçu 12)
    // Error : nom ne peut pas être vide
    // Amira Ben Ali | 22 ans | 15.5/20
    • Que se passe-t-il si l'on retire le mot-clé accessor devant note? Pourquoi un simple décorateur de champ ne suffit-il pas pour valider les affectations ?
  • Activité 5 : un mini-Angular

    • Dans src/20-mini-angular.ts, imiter le fonctionnement de @Component:
      • écrire la fabrique de décorateurs de classe Composant(options: { selecteur: string; template: string }) qui enregistre la classe et son template dans une Map indexée par le sélecteur, et affiche <selecteur> déclaré par la classe NomClasse;
      • écrire rendre(selecteur: string): string qui crée une instance de la classe enregistrée et remplace chaque {{ cle }} du template par la propriété correspondante (expression régulière /\{\{\s*(\w+)\s*\}\}/g), ou renvoie <!-- composant … inconnu -->.
    @Composant({ selecteur: 'app-titre', template: '<h1>{{ titre }}</h1>' })
    class Titre {
      titre = 'Formations 2026';
    }
    
    @Composant({ selecteur: 'app-carte', template: '<p>{{ nom }} – {{ places }} places</p>' })
    class Carte {
      nom = 'Angular';
      places = 12;
    }
    
    console.log(rendre('app-titre'));
    console.log(rendre('app-carte'));
    console.log(rendre('app-pied'));
    
    // Résultat attendu :
    // <app-titre> déclaré par la classe Titre
    // <app-carte> déclaré par la classe Carte
    // <h1>Formations 2026</h1>
    // <p>Angular – 12 places</p>
    // <!-- composant app-pied inconnu -->
    • Pourquoi les deux premières lignes s'affichent-elles avant tout appel à rendre? Que vous apprend cette activité sur ce que fait réellement @Component?
    • Faire un commit après chaque activité, puis un push : git commit -m "Décorateurs : activité N".
  • Erreurs fréquentes

  • Message ou symptôme Cause Solution
    SyntaxError: Invalid or unexpected token avec node fichier.ts Node.js ne sait pas exécuter les décorateurs Compiler avec npx tsc, puis exécuter node dist/fichier.js
    error TS1206: Decorators are not valid here. Décorateur placé devant une fonction isolée ou une variable Décorer uniquement une classe ou un membre de classe
    error TS1241: Unable to resolve signature of method decorator… + Argument of type '() => number' is not assignable to parameter of type 'number' Fabrique utilisée sans parenthèses : @arrondir Écrire @arrondir(2)
    error TS1241: … The runtime will invoke the decorator with 2 arguments, but the decorator expects 3. Signature expérimentale (cible, cle, descripteur) sans experimentalDecorators Utiliser la signature standard (methode, context), ou activer l'option (projets Angular)
    error TS1240: Unable to resolve signature of property decorator… Décorateur de méthode ou d'accessor placé sur un simple champ Décorer le bon élément ; pour valider, écrire @borne(0, 20) accessor note = 10;
    error TS2683: 'this' implicitly has type 'any'… Fonction fléchée renvoyée par le décorateur Renvoyer une function avec un paramètre this typé
    error TS2684: The 'this' context of type 'void' is not assignable to method's 'this' … Méthode d'origine appelée sans son objet : methode(...args) (à l'exécution : TypeError) Appeler methode.call(this, ...args)
    error TS2355: A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value. La fonction de remplacement oublie le return (la méthode renverrait undefined) Renvoyer le résultat de la méthode d'origine
    Property 'x' does not exist on type … pour une propriété ajoutée par un décorateur de classe Un décorateur ne modifie pas le type de la classe Déclarer la propriété dans la classe, ou utiliser l'héritage
    1. À retenir

      • Un décorateur est une fonction appelée avec @ devant une classe ou un membre ; il s'exécute une fois, à la définition de la classe.
      • Il reçoit l'élément décoré et un objet context (kind, name, static, private, addInitializer) ; s'il renvoie une valeur, elle remplace l'élément.
      • Méthode : renvoyer une function qui appelle methode.call(this, ...args). Classe : enregistrer ou étendre. Champ : transformer la valeur initiale. accessor: intercepter lectures et affectations.
      • Une fabrique @nom(parametres) renvoie le décorateur ; plusieurs décorateurs sont évalués de haut en bas et appliqués de bas en haut.
      • Angular décrit ses classes avec @Component, @Injectable, @Directive, @Pipe; les décorateurs de membres cèdent la place à input(), output(), viewChild(), inject().
      • Décorateurs standard (par défaut) et expérimentaux (experimentalDecorators, utilisés par Angular) ont des signatures incompatibles.
    Un décorateur, c'est un emballage : la méthode reste la même à l'intérieur, mais on peut ajouter autour d'elle un journal, un cache ou une validation, en une seule ligne et sans jamais réécrire son code.