Visibilité · Classes abstraites · implements
« Je respecte ce contrat » — quand une classe implements une interface, TypeScript vérifie chaque promesse.
1. Les 4 modificateurs de visibilité
public, private, protected, readonly — avec analogies concrètes.
2. Le raccourci constructeur
constructor(public name: string) — moins de code, même résultat.
3. Classes abstraites
Le modèle qu'on ne peut pas instancier directement.
4. implements : le contrat
Une classe qui respecte une interface. TypeScript refuse si une méthode manque.
Combiner generics et classes — construire du code robuste et réutilisable.
Rappel des classes JS
Mois 2 revisité — en TypeScript on ajoute visibilité et typage strict.
Les 4 modificateurs de visibilité
public, private, protected, readonly avec analogies.
Le raccourci constructeur
Écrire moins, exprimer plus — l'utiliser systématiquement.
Classes abstraites
Le moule qu'on ne peut pas instancier — pourquoi et quand.
implements : je respecte ce contrat
La différence fondamentale avec extends — le concept clé de la leçon.
JavaScript
class Compte {
// Pas de types
// Pas de visibilité
solde = 0;
deposer(montant) {
this.solde += montant;
}
}
TypeScript — on ajoute :
class Compte {
private solde: number = 0;
public deposer(montant: number): void {
this.solde += montant;
}
}
✅ Typage strict + visibilité = code sûr et prévisible
Qui peut voir et modifier quoi ?
public
Tout le monde peut y accéder.
private
Réservé à la classe elle-même.
protected
La classe + ses sous-classes.
readonly
On peut lire, jamais modifier.
🏪 Analogie : la vitrine d'un magasin — tout le monde peut voir et utiliser.
class Utilisateur {
public nom: string;
constructor(nom: string) {
this.nom = nom;
}
}
const u = new Utilisateur("Alice");
console.log(u.nom); // "Alice" ✅
💡 public est le comportement par défaut en TypeScript — on peut l'omettre, mais l'écrire explicitement clarifie le code.
🏦 Analogie : le coffre-fort d'une banque — seule la banque peut modifier le solde.
class CompteBancaire {
private solde: number = 0;
public deposer(montant: number): void {
this.solde += montant;
}
public getSolde(): number {
return this.solde;
}
}
compte.solde = 1000000; // ❌ ERREUR
Personne ne peut modifier directement le solde.
compte.deposer(500); // ✅ OK
On passe par la méthode publique.
👨👩👧 Analogie : les recettes de famille — partagées avec les enfants, pas avec les étrangers.
class Animal {
protected nom: string;
constructor(nom: string) {
this.nom = nom;
}
}
class Chien extends Animal {
aboyer() {
// ✅ accessible ici
console.log(this.nom);
}
}
// Dehors :
const c = new Chien("Rex");
c.nom; // ❌ Erreur !
⚠️ Piège courant : protected ≠ private — les sous-classes Y ont accès, pas le code extérieur.
🪪 Analogie : le numéro de carte d'identité — on peut le lire, jamais le changer.
class Produit {
readonly id: string;
public nom: string;
constructor(id: string, nom: string) {
this.id = id;
this.nom = nom;
}
}
p.nom = "Nouveau"; // ✅
p.id = "xyz"; // ❌ Erreur !
Cannot assign to 'id' because it is a read-only property.
| Modificateur | Classe elle-même | Sous-classes | Extérieur |
|---|---|---|---|
| public | ✅ | ✅ | ✅ |
| protected | ✅ | ✅ | ❌ |
| private | ✅ | ❌ | ❌ |
| readonly | 👁️ lecture | 👁️ lecture | 👁️ lecture |
💡 Règle pratique : commencer par private, rendre public uniquement ce qui est nécessaire.
Écrire moins de code, exprimer plus
TypeScript permet de déclarer ET initialiser les propriétés directement dans le constructor.
❌ Version longue (à éviter)
class Personne {
// 1. Déclarer
public nom: string;
private age: number;
constructor(
nom: string,
age: number
) {
// 2. Assigner
this.nom = nom;
this.age = age;
}
}
✅ Version raccourcie (recommandée)
class Personne {
constructor(
public nom: string,
private age: number
) {}
}
Même résultat — TypeScript fait les 3 étapes automatiquement : déclarer, recevoir, assigner.
class Produit {
constructor(
public nom: string,
private prix: number,
readonly id: string
) {}
}
const p = new Produit("Livre", 25, "P001");
console.log(p.nom); // "Livre" ✅
console.log(p.prix); // ❌ private !
🎯 À utiliser systématiquement — c'est le style idiomatique TypeScript.
Le modèle qu'on ne peut pas instancier directement
🏗️ Comme un plan d'architecte : il définit la structure, mais on ne « l'habite » pas directement — on construit d'après lui.
abstract class Forme {
// Méthode concrète — partagée par tous
public decrire(): string {
return `Je suis une forme`;
}
// Méthode abstraite — chaque sous-classe DOIT la définir
abstract aire(): number;
}
Méthode concrète
Implémentée dans la classe abstraite, héritée automatiquement.
Méthode abstraite
Juste une signature — les sous-classes doivent l'implémenter.
❌ L'erreur classique
const f = new Forme();
// Cannot create an instance
// of an abstract class.
TypeScript refuse à la compilation.
✅ La bonne façon : étendre
class Cercle extends Forme {
constructor(private rayon: number) {
super();
}
aire(): number {
return Math.PI * this.rayon ** 2;
}
}
abstract class Employe {
constructor(public nom: string) {}
abstract calculerSalaire(): number;
public afficher(): void {
console.log(`${this.nom} gagne ${this.calculerSalaire()}€`);
}
}
class Freelance extends Employe {
constructor(nom: string, private tauxHoraire: number, private heures: number) {
super(nom);
}
calculerSalaire(): number { return this.tauxHoraire * this.heures; }
}
✅ Si Freelance n'implémente pas calculerSalaire(), TypeScript refuse de compiler.
« Je respecte ce contrat »
Une interface définit le contrat. Une classe qui dit implements s'engage à le respecter intégralement. TypeScript vérifie chaque promesse.
1️⃣ Le contrat (interface)
interface Serialisable {
serialiser(): string;
deserialiser(data: string): void;
}
2️⃣ La classe qui respecte le contrat
class Config implements Serialisable {
constructor(public data: string = "") {}
serialiser(): string {
return this.data;
}
deserialiser(d: string): void {
this.data = d;
}
}
✅ Si Config oublie deserialiser(), TypeScript refuse immédiatement avec une erreur claire.
interface Serialisable {
serialiser(): string;
deserialiser(data: string): void;
}
class Config implements Serialisable {
serialiser(): string { return ""; }
// oups, on a oublié deserialiser()
}
// ❌ Erreur : Class 'Config' incorrectly implements interface 'Serialisable'.
// Property 'deserialiser' is missing.
🎯 C'est exactement le but : TypeScript refuse de compiler et vous dit précisément quelle promesse n'est pas tenue.
C'est la vraie puissance d'implements — des classes très différentes peuvent respecter le même contrat.
interface Payable {
payer(montant: number): void;
}
class CarteBancaire implements Payable {
payer(montant: number): void { console.log(`CB: ${montant}€`); }
}
class PayPal implements Payable {
payer(montant: number): void { console.log(`PayPal: ${montant}€`); }
}
function encaisser(moyen: Payable, montant: number) {
moyen.payer(montant);
}
extends
Héritage
→ Une classe hérite d'une autre classe
→ Récupère le code (méthodes, propriétés)
→ class Chien extends Animal
class B extends A {
// hérite tout de A
}
implements
Contrat
→ Une classe respecte une interface
→ Aucun code partagé — juste une forme
→ class CB implements Payable
class B implements C {
// DOIT écrire les méthodes
}
💡 Règle générale : préférer implements (interfaces) à extends (classes abstraites) — plus flexible, moins couplé.
Les generics rendent une classe réutilisable pour n'importe quel type.
class Boite<T> {
private contenu: T;
constructor(valeur: T) {
this.contenu = valeur;
}
public ouvrir(): T {
return this.contenu;
}
}
const boiteNum = new Boite<number>(42);
const boiteStr = new Boite<string>("Bonjour");
✅ Une seule classe, utilisable avec number, string, ou n'importe quel type — sans dupliquer le code.
interface Stockage<T> {
sauvegarder(item: T): void;
charger(): T;
}
class LocalStorage<T> implements Stockage<T> {
private donnee: T | undefined;
sauvegarder(item: T): void { this.donnee = item; }
charger(): T { return this.donnee as T; }
}
const s = new LocalStorage<string>();
s.sauvegarder("config");
const val = s.charger(); // string
TypeScript sait que val est un string — l'autocomplétion fonctionne.
❌ Confondre protected et private
private
Accessible : classe uniquement
Sous-classes : ❌ non
protected
Accessible : classe + sous-classes
Sous-classes : ✅ oui
❌ Oublier qu'une abstract class ne peut pas être instanciée
const f = new Forme(); // ❌ Erreur de compilation
→ Montrer l'erreur, puis créer une sous-classe concrète.
❌ Confondre extends (héritage) et implements (contrat)
extends = je copie le code · implements = je m'engage à fournir ces méthodes
interface Serialisable {
serialiser(): string;
}
abstract class BaseModel {
constructor(public id: number) {}
}
class User
extends BaseModel
implements Serialisable
{
serialiser(): string {
return `User#${this.id}`;
}
}
✅ User hérite de BaseModel ET respecte le contrat Serialisable — les deux peuvent coexister.
🔒 private protège les données internes
Personne ne peut faire compte.solde = 1000000 — on passe toujours par les méthodes publiques.
⚡ Le raccourci constructeur — l'utiliser systématiquement
constructor(public nom: string, private age: number) {} réduit énormément le code.
📋 implements = « je respecte ce contrat »
Si j'oublie une méthode, TypeScript refuse. Le compilateur est votre garde-fou.
🏆 Préférer implements (interfaces) aux classes abstraites en général
Plus flexible — une classe peut respecter plusieurs contrats à la fois.
public
Tout le monde y accède
private
La classe uniquement
protected
Classe + sous-classes
readonly
Lecture seule
abstract class
Modèle — on ne l'instancie pas
implements
« Je respecte ce contrat »
1. Compte bancaire sécurisé
Créer une classe CompteBancaire avec private solde, méthodes deposer() et retirer(), et utiliser le raccourci constructeur.
2. Hiérarchie de formes
Créer une abstract class Forme avec méthode abstraite aire(), puis implémenter Cercle et Rectangle.
3. Interface de notification
Définir une interface Notifiable avec méthode envoyer(message: string): void, puis créer Email et SMS qui l'implémentent.
4. Dépôt générique
Créer une interface Repository<T> avec méthodes save(item: T) et findById(id: number): T, et une classe qui l'implémente.
Visibilité · Classes abstraites · implements
« Je respecte ce contrat »
Avec implements, TypeScript devient votre meilleur allié : il vérifie chaque promesse avant même que votre code ne tourne.