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.
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
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.
// 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.
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.
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.
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.
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.
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
- 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 readonlyinterdit la réassignation après initialisationextendshérite d'une classe ;super()est obligatoire dans le constructeur enfantimplementsgarantit qu'une classe respecte le contrat d'une interface- Une classe
abstractne 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