TypeScript · Migration · Semaine 9

Migration JS → TS

Reprendre le carnet de contacts JavaScript et le migrer en TypeScript

Appliquer tout ce qu'on a appris cette semaine

Utilisez les flèches, cliquez ou glissez pour naviguer

Objectifs de la leçon

1. Revoir les concepts TypeScript

Types de base, tableaux/tuples, unions, interfaces, implements

2. Migrer un projet JS vers TS

Workflow progressif en 4 étapes

3. Appliquer interfaces & annotations

Sur un projet réel : le carnet de contacts

4. Gérer les valeurs optionnelles

Optional chaining ?. et nullish coalescing ??

Objectif final : comprendre que la migration est progressive — pas besoin de tout faire d'un coup.

Plan du cours

1

Récap de la semaine

Types de base, tableaux/tuples, unions, interfaces, implements

2

La stratégie de migration JS → TS

4 étapes progressives pour migrer un projet

3

Live coding : migrer le carnet de contacts

Renommer, tsconfig, interfaces, annotations — ensemble

4

Optional chaining & nullish coalescing

?. et ?? pour gérer les valeurs optionnelles

5

Bilan & aperçu

Ce qu'on sait faire, pièges courants, aperçu de la semaine 10 (generics)

Récap : tout ce qu'on a appris

Une semaine de TypeScript en un coup d'œil

Avant de migrer, revoyons les outils à notre disposition.

Types de base & annotations

let nom: string = "Alice";

let age: number = 25;

let actif: boolean = true;

// Inference : TypeScript devine le type

let ville = "Paris"; // string, pas besoin d'annoter

Avec annotation

Quand le type n'est pas évident : paramètres de fonction, objets complexes

Sans annotation (inference)

Variables locales simples : TypeScript devine, on ne répète pas

💡 Règle : annoter les fonctions, laisser l'inference pour les variables locales.

Tableaux, Tuples & Unions

Tableaux

string[]

number[]

Contact[]

Liste de valeurs du même type

Tuples

[string, number]

[string, number, boolean]

Tableau à longueur fixe, types par position

Unions

string | number

string | null

Une valeur parmi plusieurs types

💡 Les unions sont partout dans la migration : string | undefined pour les propriétés optionnelles.

Interfaces : le contrat

interface Contact {

id: number;

nom: string;

email: string;

telephone?: string; // optionnel

}

Propriétés obligatoires

id, nom, email — doivent être présents

Propriétés optionnelles

telephone? — le ? autorise l'absence

💡 Définir les interfaces en premier, c'est structurer sa réflexion avant de coder.

implements : la promesse

interface Carnet {

contacts: Contact[];

ajouter(c: Contact): void;

chercher(nom: string): Contact | undefined;

}

class MonCarnet implements Carnet {

// Obligation d'implémenter tout ce qui est dans Carnet

}

L'interface est un contrat, implements est une promesse

Si la classe ne respecte pas le contrat → erreur à la compilation

💡 Le concept clé de la semaine : interface = contrat, implements = promesse.

La migration en 4 étapes

De JavaScript à TypeScript, progressivement

Pas besoin de tout faire d'un coup — chaque étape apporte déjà des bénéfices.

Les 4 étapes de la migration

1️⃣

Renommer .js.ts

TypeScript accepte le JS valide — le code fonctionne encore

2️⃣

Ajouter tsconfig.json

Configurer TypeScript, activer strict: true

3️⃣

Définir les interfaces

Structurer les données avant d'annoter les fonctions

4️⃣

Annoter les fonctions

Paramètres et valeurs de retour — TypeScript trouve les bugs

Étapes 1 & 2 : Renommer et configurer

1️⃣ Renommer les fichiers

carnet.js

carnet.ts ✅

Le code JS reste valide en TS — les erreurs apparaissent progressivement

2️⃣ tsconfig.json

{

"compilerOptions": {

"strict": true,

"target": "ES2020"

}

}

strict: true = tout le but de TypeScript

⚠️ Ne jamais oublier strict: true — sans ça, TypeScript est permissif et rate des bugs.

Étapes 3 & 4 : Interfaces et annotations

3️⃣ Interfaces d'abord

Pourquoi commencer par les interfaces ?

  • ✅ Ça structure la réflexion
  • ✅ Ça documente les données
  • ✅ Ça guide les annotations
  • ✅ TypeScript vérifie la cohérence

4️⃣ Annoter les fonctions

Quoi annoter en priorité ?

  • ✅ Paramètres de fonction
  • ✅ Valeurs de retour
  • ✅ Propriétés d'objets complexes
  • ❌ Pas les variables locales (inference)

💡 L'ordre compte : interfaces → annotations. Les interfaces guident tout le reste.

Live Coding

Migrer le carnet de contacts JS → TS ensemble

On reprend le projet S06 et on applique les 4 étapes.

Le carnet de contacts (JavaScript)

// carnet.js — notre point de départ

const contacts = [

{ id: 1, nom: "Alice", email: "alice@mail.com" },

{ id: 2, nom: "Bob", email: "bob@mail.com", telephone: "0601020304" }

];

function ajouter(contact) {

contacts.push(contact);

}

function chercher(nom) {

return contacts.find(c => c.nom === nom);

}

function supprimer(id) {

const index = contacts.findIndex(c => c.id === id);

if (index !== -1) contacts.splice(index, 1);

}

Problème : aucune vérification de type !

ajouter("n'importe quoi") — JavaScript accepte sans broncher.

Étape 1 : Renommer .js → .ts

$ mv carnet.js carnet.ts

Que se passe-t-il ?

✅ Le code fonctionne encore — TS accepte le JS valide

⚠️ Des erreurs apparaissent : types implicites, any détectés

carnet.ts:4:18 - error TS7006: Parameter 'contact' implicitly

has an 'any' type.

carnet.ts:7:22 - error TS7006: Parameter 'nom' implicitly

has an 'any' type.

💡 Ces erreurs sont une bonne chose — TypeScript nous montre ce qu'il faut annoter !

Étape 2 : tsconfig.json

{

"compilerOptions": {

"strict": true,

"target": "ES2020",

"module": "commonjs",

"esModuleInterop": true,

"outDir": "./dist"

}

}

⚠️ Pourquoi strict: true est indispensable ?

Sans strict, TypeScript permet les any implicites — on rate les bugs qu'on voulait attraper !

💡 strict: true active : noImplicitAny, strictNullChecks, strictFunctionTypes, et plus.

Étape 3 : Interface Contact

interface Contact {

id: number;

nom: string;

email: string;

telephone?: string; // optionnel !

}

✅ Avec l'interface

const c: Contact = {

id: 1, nom: "Alice",

email: "a@mail.com"

}; // OK ✓

❌ Sans l'interface

const c = {

id: 1, nom: "Alice"

}; // email manquant!

💡 L'interface Contact est le contrat : tout contact DOIT avoir id, nom, email. Le telephone est optionnel.

Étape 3 : Interface Carnet

interface Carnet {

contacts: Contact[];

ajouter(contact: Contact): void;

chercher(nom: string): Contact | undefined;

supprimer(id: number): boolean;

}

Pourquoi une interface Carnet ?

  • • Elle documente les opérations disponibles
  • • Elle permet à une classe de implements Carnet
  • chercher retourne Contact | undefined — le contact peut ne pas exister !

💡 Contact | undefined : l'union type force à gérer le cas où le contact n'existe pas.

Étape 4 : Annoter les fonctions

// Avant (JS) → Après (TS)

function ajouter(contact: Contact): void {

contacts.push(contact);

}

function chercher(nom: string): Contact | undefined {

return contacts.find(c => c.nom === nom);

}

function supprimer(id: number): boolean {

const index = contacts.findIndex(c => c.id === id);

if (index !== -1) {

contacts.splice(index, 1);

return true;

}

return false;

}

✅ Chaque paramètre a un type, chaque fonction a un retour — TypeScript vérifie tout !

Résultat : JS vs TS côte à côte

❌ JavaScript

function ajouter(contact) {

contacts.push(contact);

}

→ Aucune vérification, accepte n'importe quoi

✅ TypeScript

function ajouter(

contact: Contact

): void {

contacts.push(contact);

}

→ TypeScript vérifie que contact est un Contact valide

La migration a trouvé des bugs potentiels !

TypeScript nous a forcés à réfléchir : que se passe-t-il si chercher ne trouve rien ? → undefined

Gérer les valeurs optionnelles

?. et ??

Quand une propriété peut être undefined ou null, ces opérateurs sont indispensables.

Optional chaining ?.

❌ Sans ?. — crash possible

const resultat = chercher("Charlie");

// resultat est undefined !

console.log(resultat.telephone);

// TypeError: Cannot read property

// of undefined 💥

✅ Avec ?. — accès sûr

const resultat = chercher("Charlie");

// resultat est undefined

console.log(resultat?.telephone);

// undefined — pas de crash ✓

?. vérifie si la valeur à gauche existe avant d'accéder à la propriété. Si elle n'existe pas → undefined au lieu d'un crash.

Nullish coalescing ??

// Fournir une valeur par défaut quand c'est null ou undefined

const tel = contact.telephone ?? "Non renseigné";

// Si telephone est undefined → "Non renseigné"

// Si telephone est "0601020304" → "0601020304"

|| — le piège

const tel = contact.telephone || "Non renseigné";

// Si telephone est "" → "Non renseigné"

// ⚠️ Chaîne vide est falsy!

?? — le bon choix

const tel = contact.telephone ?? "Non renseigné";

// Si telephone est "" → ""

// ✓ Chaîne vide est gardée!

?? ne réagit qu'à null et undefined. || réagit à tous les falsy (0, "", false).

Application au carnet de contacts

// Afficher le téléphone d'un contact trouvé

function afficherTelephone(nom: string): string {

const contact = chercher(nom);

return contact?.telephone ?? "Non renseigné";

}

// Afficher tous les contacts

function afficherTous(): void {

contacts.forEach(c => {

console.log(`${c.nom} — ${c.telephone ?? "N/A"}`);

});

}

Combo gagnant : ?. pour accéder sans crash + ?? pour fournir une valeur par défaut.

Pièges courants

❌ Tout annoter — même les variables locales

let x: number = 5; // inutile

let x = 5; // inference ✓

✅ L'inference suffit pour les variables simples — annoter les fonctions, pas les locales.

❌ Oublier strict: true dans tsconfig.json

Sans strict, TypeScript permet les any implicites et les null non vérifiés.

✅ Toujours activer strict: true — c'est tout le but de la migration !

💡 La migration semble fastidieuse ?

Rappelez-vous : TypeScript trouve des vrais bugs pendant la migration. Chaque erreur corrigée est un bug qui n'arrivera jamais en production.

À retenir !

🔄 Migration progressive

Pas besoin de tout faire d'un coup — chaque étape apporte déjà des bénéfices

📋 Interfaces en premier

Définir les interfaces structure la réflexion et guide les annotations

🔗 ?. et ??

Des outils essentiels pour gérer les propriétés optionnelles proprement

📜 Le concept clé

interface est un contrat, implements est une promesse

Après cette semaine, vous savez :

Annoter · Structurer avec des interfaces · Migrer un projet JS · Gérer les optionnels

La semaine prochaine...

Generics

Écrire du code qui fonctionne pour plusieurs types

function premier<T>(tab: T[]): T | undefined {

return tab[0];

}

Le T est un "trou" qu'on remplit au moment de l'appel

Les generics sont la suite logique : après avoir fixé les types, on les rend réutilisables.

Questions ?

L'interface est un contrat, implements est une promesse

La migration JS → TS est progressive — chaque étape rend votre code plus sûr.