Leçon 5 / 8
Leçon 05 · TypeScript

Classes et POO

Classes TypeScript vs JavaScript

TypeScript enrichit les classes JavaScript avec le typage des propriétés et des paramètres de constructeur. Tu déclares les propriétés en haut de la classe, avec leur type, avant le constructeur.

TypeScript — classe de base
class Utilisateur {
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age  = age;
  }

  saluer(): string {
    return `Bonjour, je suis ${this.name} !`;
  }
}

const u = new Utilisateur("Alice", 28);
console.log(u.saluer()); // Bonjour, je suis Alice !

TypeScript vérifie que les arguments passés au constructeur correspondent aux types déclarés. Passer un number à la place du name déclenche une erreur à la compilation.

Modificateurs d'accès : public, private, protected

TypeScript ajoute trois modificateurs qui contrôlent la visibilité des membres d'une classe :

  • public — accessible depuis n'importe où (défaut si rien n'est précisé)
  • private — accessible uniquement à l'intérieur de la classe
  • protected — accessible dans la classe et dans ses sous-classes
TypeScript — modificateurs d'accès
class CompteBancaire {
  public    titulaire: string;
  private   solde: number;
  protected iban: string;

  constructor(titulaire: string, soldeInitial: number, iban: string) {
    this.titulaire = titulaire;
    this.solde     = soldeInitial;
    this.iban      = iban;
  }

  deposer(montant: number): void {
    this.solde += montant;
  }

  getSolde(): number {
    return this.solde;
  }
}

const compte = new CompteBancaire("Alice", 1000, "FR76...");
console.log(compte.titulaire);  // OK — public
// compte.solde                  // Erreur — private !
compte.deposer(500);
console.log(compte.getSolde()); // 1500
💡

Les modificateurs d'accès n'existent qu'à la compilation TypeScript. Dans le JavaScript généré, les propriétés restent accessibles. Pour une vraie encapsulation à l'exécution, utilise les champs privés natifs ES2022 (#propriete).

Shorthand constructor

TypeScript propose une syntaxe raccourcie : déclarer et initialiser les propriétés directement dans la signature du constructeur. On place le modificateur d'accès devant chaque paramètre.

TypeScript — shorthand constructor
// Forme longue (répétitive)
class PointLong {
  x: number;
  y: number;
  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
  }
}

// Forme courte (équivalente)
class Point {
  constructor(
    public x: number,
    public y: number
  ) {}
}

const p = new Point(3, 7);
console.log(p.x, p.y); // 3  7

Propriétés readonly

Le mot-clé readonly empêche la réassignation d'une propriété après initialisation. Parfait pour un identifiant unique, une date de création, etc.

TypeScript — readonly
class Produit {
  constructor(
    public readonly id: number,
    public nom: string
  ) {}
}

const p = new Produit(1, "Clavier");
p.nom = "Souris";  // OK
// p.id = 2;           // Erreur — cannot assign to 'id' because it is a read-only property

Héritage : extends et super

TypeScript hérite du système de classes ES6. Une sous-classe utilise extends et doit appeler super() dans son constructeur pour initialiser la classe parente.

TypeScript — héritage
class Animal {
  constructor(public nom: string) {}

  parler(): string {
    return `${this.nom} fait un bruit.`;
  }
}

class Chien extends Animal {
  constructor(nom: string, public race: string) {
    super(nom); // initialise this.nom
  }

  // Surcharge de méthode
  parler(): string {
    return `${this.nom} aboie !`;
  }
}

const rex = new Chien("Rex", "Labrador");
console.log(rex.parler()); // Rex aboie !
console.log(rex.race);     // Labrador

Implémenter une interface

Une classe peut implémenter une ou plusieurs interfaces avec le mot-clé implements. TypeScript vérifie alors que la classe possède bien toutes les propriétés et méthodes définies par l'interface.

TypeScript — implements
interface Animal {
  nom: string;
  parler(): string;
}

interface Domestique {
  caresser(): void;
}

// Implémentation de plusieurs interfaces à la fois
class Chat implements Animal, Domestique {
  constructor(public nom: string) {}

  parler(): string {
    return `${this.nom} ronronne.`;
  }

  caresser(): void {
    console.log("Purrrr...");
  }
}

const mimi = new Chat("Mimi");
console.log(mimi.parler()); // Mimi ronronne.
ℹ️

Différence clé : extends hérite du code et des données d'une classe parente. implements garantit qu'une classe respecte un contrat (une interface) sans héritage de code. Les deux peuvent être combinés sur la même classe.

Classes abstraites

Une classe abstraite sert de modèle : elle ne peut pas être instanciée directement. Elle définit des méthodes abstraites que chaque sous-classe doit implémenter, et peut aussi contenir des méthodes concrètes partagées.

TypeScript — abstract class
abstract class Forme {
  constructor(public couleur: string) {}

  // Méthode abstraite : chaque sous-classe doit l'implémenter
  abstract aire(): number;

  // Méthode concrète partagée par toutes les sous-classes
  decrire(): string {
    return `Forme ${this.couleur}, aire = ${this.aire().toFixed(2)}`;
  }
}

class Cercle extends Forme {
  constructor(couleur: string, public rayon: number) {
    super(couleur);
  }
  aire(): number {
    return Math.PI * this.rayon ** 2;
  }
}

class Rectangle extends Forme {
  constructor(couleur: string, public largeur: number, public hauteur: number) {
    super(couleur);
  }
  aire(): number {
    return this.largeur * this.hauteur;
  }
}

// const f = new Forme("rouge"); // Erreur — classe abstraite !
const c = new Cercle("bleu", 5);
console.log(c.decrire()); // Forme bleu, aire = 78.54

Propriétés statiques

Une propriété ou méthode static appartient à la classe elle-même, pas à ses instances. Utile pour un compteur d'instances, une valeur partagée ou une méthode utilitaire.

TypeScript — static
class Connexion {
  static count: number = 0;

  constructor(public userId: string) {
    Connexion.count++;
  }

  static reset(): void {
    Connexion.count = 0;
  }
}

new Connexion("alice");
new Connexion("bob");
new Connexion("charlie");

console.log(Connexion.count); // 3
Connexion.reset();
console.log(Connexion.count); // 0
// À retenir
  • Déclare les propriétés de classe avec leur type avant le constructeur
  • public (défaut), private (classe uniquement), protected (classe + sous-classes)
  • Le shorthand constructor (constructor(private name: string)) déclare et initialise en une ligne
  • readonly interdit la réassignation après initialisation
  • extends hérite d'une classe ; super() est obligatoire dans le constructeur enfant
  • implements garantit qu'une classe respecte le contrat d'une interface
  • Une classe abstract ne peut pas être instanciée directement ; ses méthodes abstraites sont obligatoires
  • static : propriétés et méthodes partagées par toutes les instances, accessibles via le nom de la classe