TypeScript · Classes · Visibilité · Interfaces

Maîtriser les classes

Visibilité · Classes abstraites · implements

« Je respecte ce contrat » — quand une classe implements une interface, TypeScript vérifie chaque promesse.

Objectifs de la leçon

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.

Plan du cours

1

Rappel des classes JS

Mois 2 revisité — en TypeScript on ajoute visibilité et typage strict.

2

Les 4 modificateurs de visibilité

public, private, protected, readonly avec analogies.

3

Le raccourci constructeur

Écrire moins, exprimer plus — l'utiliser systématiquement.

4

Classes abstraites

Le moule qu'on ne peut pas instancier — pourquoi et quand.

5

implements : je respecte ce contrat

La différence fondamentale avec extends — le concept clé de la leçon.

Rappel : Classes JavaScript (mois 2)

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

Les 4 modificateurs de visibilité

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.

public — Accès total

🏪 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.

private — Données internes protégées

🏦 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.

protected — La classe et ses enfants

👨‍👩‍👧 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 : protectedprivate — les sous-classes Y ont accès, pas le code extérieur.

readonly — Immuable après création

🪪 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.

Récap : qui peut accéder à quoi ?

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.

Le raccourci constructeur

Écrire moins de code, exprimer plus

TypeScript permet de déclarer ET initialiser les propriétés directement dans le constructor.

Avant vs Après le raccourci

❌ 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.

Le raccourci : ce qui se passe réellement

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.

Classes abstraites

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.

Définir une classe abstraite

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.

On ne peut pas instancier une classe abstraite

❌ 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;

}

}

Classes abstraites en pratique

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.

Le concept clé de la leçon

implements

« 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.

Interface + implements : l'exemple de base

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.

TypeScript refuse si le contrat n'est pas respecté

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.

Plusieurs classes, même contrat

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 vs implements — Ne pas confondre !

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é.

Combiner Generics et Classes

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.

Generics + implements — Le combo puissant

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.

Pièges courants à éviter

❌ 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

On peut combiner extends ET implements

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.

Points clés à retenir

🔒 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.

À retenir !

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 »

Exercices pratiques

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.

Questions ?

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.