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

Les classes en TypeScript

Cette leçon montre ce que TypeScript ajoute aux classes JavaScript : le typage des propriétés, des paramètres et des valeurs de retour, les règles d'initialisation du mode strict, les modificateurs pu...

0 chapitres 0 QCM Accès gratuit
Dans cet article

À propos de ce cours

Cette leçon montre ce que TypeScript ajoute aux classes JavaScript : le typage des propriétés, des paramètres et des valeurs de retour, les règles d'initialisation du mode strict, les modificateurs public, protected, private et readonly, et les propriétés de paramètre qui raccourcissent les constructeurs. Vous utiliserez l'héritage avec le mot-clé override, créerez des classes abstraites et des classes génériques, et verrez ce que le compilateur efface dans le JavaScript produit. La leçon se termine par un service et un composant Angular annotés, avec les conventions private, protected et readonly. Des schémas et six activités pratiques accompagnent chaque notion.
  1. Objectifs

    • Typer les propriétés, les paramètres et les valeurs de retour d'une classe
    • Comprendre les règles d'initialisation des propriétés en mode strict
    • Utiliser les modificateurs d'accès public, protected, private et le modificateur readonly
    • Écrire des classes plus courtes grâce aux propriétés de paramètre
    • Utiliser l'héritage avec le mot-clé override, et créer des classes abstraites
    • Écrire une classe générique
    • Appliquer ces notions dans les composants et les services Angular
  2. Présentation

    • TypeScript reprend les classes d'ECMAScript (voir Les classes et les modules ES6) et y ajoute ce qui manque à JavaScript pour les grands projets :
      • les types des propriétés, des paramètres et des valeurs de retour ;
      • les modificateurs d'accès (public, protected, private) et readonly;
      • les propriétés de paramètre, qui déclarent et remplissent une propriété en un seul mot ;
      • les classes et méthodes abstraites, le mot-clé override, les classes génériques.
    • Toutes ces vérifications ont lieu à la compilation. Le compilateur tsc produit ensuite un JavaScript ordinaire, d'où les types et les modificateurs ont disparu.
    • Dans Angular, tout est classe : composants, services, pipes, directives, guards. Lire et écrire une classe TypeScript est donc la compétence de base du développeur Angular.

Une classe TypeScript et son JavaScript compilé

  1. Préparer l'environnement de test

    • Créer un dossier rappel-typescript, l'initialiser avec Git et npm, et installer TypeScript dans le projet :
mkdir rappel-typescript
cd rappel-typescript
git init
npm init -y
npm install -D typescript
mkdir src
  • Créer à la racine le fichier tsconfig.json ci-dessous, et ajouter "type": "module" dans package.json. Les options strict et noImplicitOverride sont les mêmes que dans un projet Angular.
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "strict": true,
    "noImplicitOverride": true,
    "outDir": "dist",
    "rootDir": "src"
  }
}
  • On écrit les fichiers .ts dans src/, on compile tout le projet avec npx tsc, puis on exécute le JavaScript produit dans dist/:
npx tsc                  # compile src/*.ts vers dist/*.js et affiche les erreurs
node dist/stagiaire.js   # exécute le fichier compilé
npx tsc --watch          # recompile automatiquement à chaque sauvegarde
  • Git : ajouter un fichier .gitignore contenant node_modules/ et dist/, puis publier le dossier sur un dépôt GitHub rappel-typescript.
  • Typer une classe

    • Chaque propriété est déclarée avec son type ; chaque méthode indique le type de ses paramètres et de sa valeur de retour (void si elle ne renvoie rien).
    • Une propriété optionnelle s'écrit avec ?: son type devient string | undefined.
    // src/stagiaire.ts
    class Stagiaire {
      nom: string;
      prenom: string;
      groupe: string = 'Non affecté';   // valeur par défaut : le type pourrait être déduit
      notes: number[] = [];
      telephone?: string;               // optionnelle
    
      constructor(nom: string, prenom: string) {
        this.nom = nom;
        this.prenom = prenom;
      }
    
      ajouterNote(note: number): this {   // renvoie l'objet lui-même (enchaînement)
        this.notes.push(note);
        return this;
      }
    
      get moyenne(): number {
        if (this.notes.length === 0) return 0;
        return this.notes.reduce((s, n) => s + n, 0) / this.notes.length;
      }
    
      presenter(): string {
        return `${this.prenom} ${this.nom} (${this.groupe}) – ${this.moyenne.toFixed(2)}`;
      }
    }
    
    const amira = new Stagiaire('Ben Ali', 'Amira');
    amira.groupe = 'DEV1';
    amira.ajouterNote(14).ajouterNote(16).ajouterNote(17);
    console.log(amira.presenter());                       // Amira Ben Ali (DEV1) – 15.67
    console.log(amira.telephone ?? 'pas de téléphone');   // pas de téléphone
    • Le compilateur signale alors les erreurs avant l'exécution :
    Code Message de tsc
    amira.notes.push('18'); TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
    new Stagiaire('Ben Ali'); TS2554: Expected 2 arguments, but got 1.
    amira.age = 20; TS2339: Property 'age' does not exist on type 'Stagiaire'.
    amira.moyenne = 20; (accesseur sans set) TS2540: Cannot assign to 'moyenne' because it is a read-only property.
    • Une classe est aussi un type : on peut écrire const liste: Stagiaire[] = []; ou function afficher(s: Stagiaire): void.
  • L'initialisation des propriétés en mode strict

    • Avec "strict": true, chaque propriété doit recevoir une valeur, soit à sa déclaration, soit dans le constructeur. Sinon :
    class Stagiaire {
      nom: string;
      groupe: string;      // ni valeur par défaut, ni affectation dans le constructeur
      constructor(nom: string) {
        this.nom = nom;
      }
    }
    // TS2564: Property 'groupe' has no initializer and is not definitely assigned in the constructor.
    • Trois solutions, selon le sens de la propriété :
      • lui donner une valeur par défaut: groupe = 'Non affecté';;
      • la rendre optionnelle si elle peut vraiment être absente : groupe?: string;;
      • utiliser l'assertion d'affectation ! si elle est remplie plus tard par un autre mécanisme : groupe!: string;. À utiliser avec prudence : TypeScript vous fait alors confiance.
  • Les modificateurs d'accès et readonly

    • TypeScript permet de préciser qui peut utiliser chaque membre d'une classe :
      • public: accessible partout. C'est la valeur par défaut, le mot-clé est donc facultatif ;
      • protected: accessible dans la classe et dans ses classes enfants ;
      • private: accessible uniquement dans la classe ;
      • readonly: lisible partout, mais modifiable seulement à la déclaration ou dans le constructeur.

    Les modificateurs d'accès des classes TypeScript

    class Compte {
      private solde = 0;
      protected titulaire = 'Amira Ben Ali';
      readonly numero = 'TN59-0001';
    
      deposer(montant: number): void {
        this.solde += montant;      // autorisé : on est dans la classe
      }
    }
    
    const c = new Compte();
    c.deposer(100);
    c.solde = 100;        // TS2341: Property 'solde' is private and only accessible within class 'Compte'.
    c.titulaire;          // TS2445: Property 'titulaire' is protected and only accessible within class
                          //         'Compte' and its subclasses.
    c.numero = 'TN02';    // TS2540: Cannot assign to 'numero' because it is a read-only property.
    • private ou #? private est une règle de TypeScript, vérifiée seulement à la compilation : dans le JavaScript produit, la propriété reste accessible. Le champ # d'ECMAScript est protégé à l'exécution. Dans Angular, on utilise surtout private, plus lisible et suffisant puisque tout le code passe par le compilateur.
  • Les propriétés de paramètre

    • Placer un modificateur (public, protected, private ou readonly) devant un paramètre du constructeur déclare la propriété et lui affecte automatiquement la valeur reçue.

    Les propriétés de paramètre en TypeScript

    // src/formation.ts
    class Formation {
      static readonly TVA = 0.19;
      private static nombre = 0;
    
      private inscrits: string[] = [];
    
      constructor(
        public readonly titre: string,
        private capacite: number,
        protected prixHT: number = 0,
      ) {
        Formation.nombre++;
      }
    
      static get total(): number {
        return Formation.nombre;
      }
    
      get placesRestantes(): number {
        return this.capacite - this.inscrits.length;
      }
    
      get prixTTC(): number {
        return +(this.prixHT * (1 + Formation.TVA)).toFixed(3);
      }
    
      inscrire(nom: string): boolean {
        if (this.placesRestantes === 0) return false;
        this.inscrits.push(nom);
        return true;
      }
    }
    
    const web = new Formation('Développement web', 2, 1200);
    console.log(web.inscrire('Amira'), web.inscrire('Sami'), web.inscrire('Karim'));   // true true false
    console.log(web.titre, web.placesRestantes, web.prixTTC);   // Développement web 0 1428
    • C'est exactement l'écriture que vous rencontrerez dans de nombreux projets Angular pour recevoir un service : constructor(private formationService: FormationService) {}.
  • L'héritage et le mot-clé override

    • L'héritage fonctionne comme en JavaScript (extends, super). TypeScript vérifie en plus que les types restent compatibles.
    • Avec l'option noImplicitOverride (activée dans les projets Angular), toute méthode qui redéfinit une méthode de la classe parente doit porter le mot-clé override. Cela évite deux erreurs : redéfinir une méthode sans le vouloir, et croire redéfinir une méthode qui n'existe pas (faute de frappe).
    class FormationEnLigne extends Formation {
      constructor(titre: string, public plateforme: string) {
        super(titre, 100, 300);
      }
    
      override get prixTTC(): number {               // redéfinition : override obligatoire
        return +(super.prixTTC * 0.8).toFixed(3);    // 20 % de réduction en ligne
      }
    
      lien(): string {
        return `https://${this.plateforme}/${this.titre.toLowerCase().replaceAll(' ', '-')}`;
      }
    }
    
    const design = new FormationEnLigne('Infographie et design', 'moodle.formaplus.tn');
    console.log(design.prixTTC);   // 285.6 : prixHT est protected, la classe enfant peut l'utiliser
    console.log(design.lien());    // https://moodle.formaplus.tn/infographie-et-design
    console.log(Formation.total);  // 2
    Oubli Message de tsc
    override absent sur une redéfinition TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'Formation'.
    override sur une méthode qui n'existe pas dans le parent TS4113: This member cannot have an 'override' modifier because it is not declared in the base class 'Formation'.
    1. Les classes abstraites

      • Une classe abstract sert uniquement de modèle commun à d'autres classes : on ne peut pas créer d'objet directement à partir d'elle.
      • Elle peut contenir des méthodes abstraites, déclarées sans code : chaque classe enfant est obligée de les écrire.

    Une classe abstraite et ses classes enfants

    // src/paiement.ts
    abstract class Paiement {
      constructor(protected montant: number) {}
    
      abstract frais(): number;           // pas de code : à écrire dans chaque classe enfant
    
      total(): number {                   // méthode commune, qui utilise frais()
        return this.montant + this.frais();
      }
    
      recu(): string {
        return `${this.constructor.name} : ${this.montant.toFixed(3)} + ${this.frais().toFixed(3)} = ${this.total().toFixed(3)} DT`;
      }
    }
    
    class PaiementEspeces extends Paiement {
      frais(): number {
        return 0;
      }
    }
    
    class PaiementCarte extends Paiement {
      frais(): number {
        return this.montant * 0.015;
      }
    }
    
    class PaiementVirement extends Paiement {
      frais(): number {
        return 2.5;
      }
    }
    
    const paiements: Paiement[] = [new PaiementEspeces(120), new PaiementCarte(120), new PaiementVirement(120)];
    paiements.forEach((p) => console.log(p.recu()));
    // PaiementEspeces : 120.000 + 0.000 = 120.000 DT
    // PaiementCarte : 120.000 + 1.800 = 121.800 DT
    // PaiementVirement : 120.000 + 2.500 = 122.500 DT
    • Implémenter une méthode abstraite ne demande pas override: il n'y a pas de code parent à remplacer.
    Code Message de tsc
    new Paiement(120); TS2511: Cannot create an instance of an abstract class.
    class PaiementCheque extends Paiement {} TS2515: Non-abstract class 'PaiementCheque' does not implement inherited abstract member frais from class 'Paiement'.
    1. Les classes génériques

      • Une classe générique reçoit un type en paramètre, noté entre chevrons <T>. On écrit la classe une seule fois, et on l'utilise avec des types différents, sans perdre la vérification des types.
    // src/liste.ts
    class Liste<T> {
      private elements: T[] = [];
    
      ajouter(element: T): void {
        this.elements.push(element);
      }
    
      get taille(): number {
        return this.elements.length;
      }
    
      trouver(critere: (element: T) => boolean): T | undefined {
        return this.elements.find(critere);
      }
    }
    
    const noms = new Liste<string>();
    noms.ajouter('Amira');
    noms.ajouter('Sami');
    // noms.ajouter(42);   // TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
    console.log(noms.taille, noms.trouver((n) => n.startsWith('S')));   // 2 Sami
    
    interface Produit {
      id: number;
      nom: string;
      prix: number;
    }
    const catalogue = new Liste<Produit>();
    catalogue.ajouter({ id: 1, nom: 'Clavier', prix: 45 });
    console.log(catalogue.trouver((p) => p.prix < 50)?.nom);   // Clavier
    • Vous utilisez déjà des génériques sans le savoir : Array<string> (ou string[]), Promise<Produit>, et dans Angular signal<Formation[]>([]) ou Observable<Produit[]>.
    • On peut contraindre le type reçu : class Repertoire<T extends { id: number }> n'accepte que des types possédant une propriété id numérique (sinon : TS2344: Type 'string' does not satisfy the constraint '{ id: number; }'.).
  • Les classes TypeScript dans Angular

    • Un service et un composant récents, tels que la CLI d'Angular les génère et que vous les écrirez :
    // formation.service.ts
    import { Injectable } from '@angular/core';
    
    export interface Formation {
      id: number;
      titre: string;
      placesRestantes: number;
    }
    
    @Injectable({ providedIn: 'root' })
    export class FormationService {
      private readonly formations: Formation[] = [
        { id: 1, titre: 'Développement web', placesRestantes: 6 },
        { id: 2, titre: 'Réseaux informatiques', placesRestantes: 0 },
      ];
    
      lister(): Formation[] {
        return [...this.formations];
      }
    }
    // catalogue.ts
    import { Component, OnInit, computed, inject, signal } from '@angular/core';
    import { Formation, FormationService } from './formation.service';
    
    @Component({
      selector: 'app-catalogue',
      template: `
        <h2>{{ titre }} ({{ nombre() }})</h2>
        @for (f of formations(); track f.id) {
          <p>{{ f.titre }} – {{ f.placesRestantes }} places</p>
        }
      `,
    })
    export class Catalogue implements OnInit {
      private readonly service = inject(FormationService);              // privé : usage interne
    
      protected readonly titre = 'Nos formations';                      // protected : utilisé par le template
      protected readonly formations = signal<Formation[]>([]);         // classe générique : signal<T>
      protected readonly nombre = computed(() => this.formations().length);
    
      ngOnInit(): void {
        this.formations.set(this.service.lister());
      }
    
      protected reserver(id: number): void {
        this.formations.update((liste) =>
          liste.map((f) => (f.id === id ? { ...f, placesRestantes: f.placesRestantes - 1 } : f)),
        );
      }
    }
    • Les conventions à retenir pour Angular :
      • private pour ce que seule la classe utilise (les services injectés, les données internes) ;
      • protected pour ce que le template affiche ou appelle : c'est la recommandation du guide de style d'Angular, qui indique clairement que le membre sert au template, sans l'ouvrir aux autres classes ;
      • public (sans mot-clé) pour ce que d'autres classes doivent utiliser, par exemple les méthodes d'un service ;
      • readonly pour tout ce qui n'est jamais réaffecté : services injectés, signals, constantes.
    • Dans un code plus ancien, le service est reçu par une propriété de paramètre, avec la même signification :
    export class CatalogueAncien implements OnInit {
      formations: Formation[] = [];
    
      constructor(private formationService: FormationService) {}
    
      ngOnInit(): void {
        this.formations = this.formationService.lister();
      }
    }
    • À venir : implements OnInit utilise une interface (leçon suivante), et @Component / @Injectable sont des décorateurs (leçon Présentation des décorateurs).
  • Activité 1 : typer une classe JavaScript

    • Dans le projet rappel-typescript, créer src/a1-stagiaire.ts et y recopier la classe Stagiaire écrite en JavaScript dans l'activité 1 de la leçon Les classes et les modules ES6.
    • Lancer npx tsc: relever en commentaire les erreurs affichées (propriétés non déclarées, paramètres sans type…).
    • Ajouter tous les types nécessaires : propriétés, paramètres, valeurs de retour (y compris this pour ajouterNote), accesseurs. Compiler jusqu'à n'avoir plus aucune erreur, puis exécuter node dist/a1-stagiaire.js: le résultat doit être identique à celui de la version JavaScript.
    • Ajouter à la fin du fichier les quatre lignes du tableau « Typer une classe » et vérifier que tsc affiche bien les quatre messages. Les supprimer ensuite.
  • Activité 2 : une salle de formation

    • Dans src/a2-salle.ts, écrire une classe Salle avec uniquement des propriétés de paramètre dans le constructeur : code (public, en lecture seule), capacite (public) et equipee (privé, false par défaut).
    • Ajouter : un compteur statique privé lisible par l'accesseur statique total, une liste privée de créneaux réservés, l'accesseur nombreReservations, une méthode reserver(creneau: string, effectif: number): string et une méthode description(): string.
    • Tester :
    const b12 = new Salle('B12', 24, true);
    const a3 = new Salle('A3', 16);
    console.log(b12.description());
    console.log(a3.description());
    console.log(b12.reserver('lundi 8h', 20));
    console.log(b12.reserver('lundi 8h', 18));
    console.log(a3.reserver('mardi 14h', 20));
    console.log(b12.nombreReservations, Salle.total);
    
    // Résultat attendu :
    // Salle B12 – 24 places – équipée
    // Salle A3 – 16 places
    // B12 : réservée le lundi 8h
    // B12 : lundi 8h déjà réservé
    // A3 : refusé, 20 > 16 places
    // 1 2
    • Écrire ensuite b12.code = 'B13'; et b12.equipee;: noter les deux messages du compilateur. Ouvrir dist/a2-salle.js: que sont devenus readonly et private?
  • Activité 3 : héritage avec protected et override

    • Dans src/a3-personnes.ts, écrire une classe Personne avec les propriétés de paramètre protected nom et protected prenom, et une méthode presenter(): string.
    • Écrire les classes Stagiaire (propriété groupe) et Formateur (propriété specialite) qui redéfinissent presenter() en réutilisant super.presenter().
    • Compiler sans le mot-clé override, noter l'erreur TS4114, puis l'ajouter. Écrire volontairement override presentr() (faute de frappe) : quel message obtenez-vous ? En quoi override vous protège-t-il ?
    • Essayer d'afficher stagiaire.nom depuis l'extérieur : quel message ? Comment exposer le nom en lecture seulement, sans le rendre public? (indice : un accesseur get).
  • Activité 4 : une classe abstraite de notification

    • Dans src/a4-notifications.ts, écrire une classe abstraite Notification avec une propriété de paramètre protected readonly destinataire: string, une méthode abstraite envoyer(message: string): string et une méthode protégée horodatage() qui renvoie '[29/10 09:30]'.
    • Écrire trois classes enfants : NotificationEmail, NotificationSms (message coupé à 20 caractères suivis de « … ») et NotificationPush (message en majuscules).
    • Ranger une instance de chaque classe dans un tableau de type Notification[], puis envoyer le même message à tous :
    for (const n of notifications) {
      console.log(n.envoyer('Votre séance de demain est reportée à 10h'));
    }
    
    // Résultat attendu :
    // [29/10 09:30] E-mail à amira@exemple.tn : Votre séance de demain est reportée à 10h
    // [29/10 09:30] SMS au +216 20 000 000 : Votre séance de dema…
    // [29/10 09:30] Notification sur l'appareil de Sami : VOTRE SÉANCE DE DEMAIN EST REPORTÉE À 10H
    • Ajouter une classe NotificationCourrier sans méthode envoyer() et noter le message TS2515. Essayer new Notification('x') et noter le message TS2511.
  • Activité 5 : un répertoire générique

    • Dans src/a5-repertoire.ts, écrire une classe générique Repertoire<T extends { id: number }> avec une liste privée et :
      • ajouter(...nouveaux: T[]): this (paramètre rest, enchaînable) ;
      • trouverParId(id: number): T | undefined;
      • supprimer(id: number): boolean (renvoie true si un élément a été supprimé) ;
      • un accesseur tous de type readonly T[].
    • Déclarer deux interfaces Stagiaire (id, nom, groupe) et Livre (id, titre, auteur), puis tester :
    const stagiaires = new Repertoire<Stagiaire>().ajouter(
      { id: 1, nom: 'Amira', groupe: 'DEV1' },
      { id: 2, nom: 'Sami', groupe: 'DEV2' },
    );
    const livres = new Repertoire<Livre>().ajouter({ id: 10, titre: 'Apprendre Angular', auteur: 'Ali Gharbi' });
    
    console.log(stagiaires.trouverParId(2)?.nom, stagiaires.trouverParId(5)?.nom ?? 'introuvable');
    console.log(stagiaires.supprimer(1), stagiaires.supprimer(1), stagiaires.tous.length);
    console.log(livres.trouverParId(10)?.titre);
    
    // Résultat attendu :
    // Sami introuvable
    // true false 1
    // Apprendre Angular
    • Essayer new Repertoire<string>() et stagiaires.ajouter({ id: 3, titre: 'x', auteur: 'y' }): expliquer les deux messages du compilateur en commentaire.
  • Activité 6 : retour sur le projet Angular FormaPlus

    • Ouvrir le projet forma-plus du TP 1. Dans chaque composant, ajouter les modificateurs adaptés : protected readonly pour les données affichées par le template et jamais réaffectées (chiffres, formations, slogan…), private pour ce qui ne sert qu'à la classe.
    • Lancer ng build: l'application doit compiler sans erreur.
    • Passer ensuite slogan en private dans Entete et relancer ng build. Avec les versions récentes d'Angular (le TP a été réalisé avec Angular 22), la compilation réussit : le template a accès aux membres privés. Expliquer en commentaire pourquoi le guide de style d'Angular recommande pourtant protected pour les membres utilisés uniquement par le template (indice : que comprend une personne qui lit private dans la classe ?). Remettre protected readonly.
    • Faire un commit dans chaque dépôt : git commit -m "Classes TypeScript : activité N", puis un push.
  • Erreurs fréquentes

  • Message Cause Solution
    TS2564: Property 'x' has no initializer … Propriété jamais initialisée (mode strict) Valeur par défaut, affectation dans le constructeur, ? ou !
    TS2339: Property 'x' does not exist on type 'Y'. Propriété non déclarée dans la classe, ou faute de frappe Déclarer la propriété avec son type
    TS2341: Property 'x' is private … Accès à un membre private depuis l'extérieur Passer par une méthode ou un accesseur, ou revoir le modificateur
    TS2445: Property 'x' is protected … Accès à un membre protected hors de la classe et de ses enfants Idem
    TS2540: Cannot assign to 'x' because it is a read-only property. Modification d'une propriété readonly ou d'un accesseur sans set Créer un nouvel objet, ou retirer readonly si la valeur doit vraiment changer
    TS4114: This member must have an 'override' modifier … Redéfinition sans override (noImplicitOverride) Ajouter override devant la méthode
    TS2511: Cannot create an instance of an abstract class. new sur une classe abstraite Instancier une classe enfant
    TS2515: Non-abstract class 'X' does not implement inherited abstract member … Méthode abstraite non écrite dans la classe enfant Écrire la méthode dans la classe enfant
    TS2355: A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value. Type de retour annoncé, mais aucun return Ajouter le return, ou déclarer void
    1. À retenir

      • En TypeScript, on type les propriétés, les paramètres et les valeurs de retour ; une classe est aussi un type.
      • En mode strict, chaque propriété doit être initialisée, ou déclarée optionnelle avec ?.
      • public (par défaut), protected, private et readonly sont vérifiés à la compilation, puis effacés.
      • Une propriété de paramètre (constructor(private x: T)) déclare et remplit une propriété en une fois.
      • override signale une redéfinition ; une classe abstract impose ses méthodes abstraites à ses enfants ; une classe générique <T> s'adapte au type reçu.
      • Dans Angular : private readonly pour les services injectés, protected pour ce qu'affiche le template.
    TypeScript ne change pas ce que fait une classe : il vérifie, avant l'exécution, que vous l'utilisez comme prévu. Chaque erreur signalée par tsc est un bug évité dans le navigateur.