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
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.
Récap de la semaine
Types de base, tableaux/tuples, unions, interfaces, implements
La stratégie de migration JS → TS
4 étapes progressives pour migrer un projet
Live coding : migrer le carnet de contacts
Renommer, tsconfig, interfaces, annotations — ensemble
Optional chaining & nullish coalescing
?. et ?? pour gérer les valeurs optionnelles
Bilan & aperçu
Ce qu'on sait faire, pièges courants, aperçu de la semaine 10 (generics)
Une semaine de TypeScript en un coup d'œil
Avant de migrer, revoyons les outils à notre disposition.
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
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.
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.
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.
De JavaScript à TypeScript, progressivement
Pas besoin de tout faire d'un coup — chaque étape apporte déjà des bénéfices.
Renommer .js → .ts
TypeScript accepte le JS valide — le code fonctionne encore
Ajouter tsconfig.json
Configurer TypeScript, activer strict: true
Définir les interfaces
Structurer les données avant d'annoter les fonctions
Annoter les fonctions
Paramètres et valeurs de retour — TypeScript trouve les bugs
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.
3️⃣ Interfaces d'abord
Pourquoi commencer par les interfaces ?
4️⃣ Annoter les fonctions
Quoi annoter en priorité ?
💡 L'ordre compte : interfaces → annotations. Les interfaces guident tout le reste.
Migrer le carnet de contacts JS → TS ensemble
On reprend le projet S06 et on applique les 4 étapes.
// 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.
$ 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 !
{
"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.
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.
interface Carnet {
contacts: Contact[];
ajouter(contact: Contact): void;
chercher(nom: string): Contact | undefined;
supprimer(id: number): boolean;
}
Pourquoi une interface Carnet ?
💡 Contact | undefined : l'union type force à gérer le cas où le contact n'existe pas.
// 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 !
❌ 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
?. et ??
Quand une propriété peut être undefined ou null, ces opérateurs sont indispensables.
❌ 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.
// 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).
// 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.
❌ 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.
🔄 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
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.
L'interface est un contrat, implements est une promesse
La migration JS → TS est progressive — chaque étape rend votre code plus sûr.