TypeScript pour les Développeurs Web : Maîtrisez la Sécurité des Types
Découvrez comment TypeScript transforme votre code JavaScript en le rendant plus sûr et maintenable. Ce cours vous guide à travers les fondamentaux du typage pour devenir un développeur web moderne et confiant.
1. Qu'est-ce que TypeScript et pourquoi l'apprendre ?
Définition : TypeScript est un sur-ensemble de JavaScript qui ajoute un système de types statiques. Imaginez TypeScript comme une évolution de JavaScript où vous déclarez explicitement le type de données (nombre, texte, objet) que vos variables doivent contenir.
Explication détaillée :
JavaScript traditionnel est très flexible - vous pouvez mettre n'importe quelle valeur dans n'importe quelle variable. C'est pratique au départ, mais cela crée des bugs difficiles à détecter. TypeScript agit comme un "gardien" qui vérifie vos données avant même que votre code s'exécute.
Pensez à TypeScript comme à un système de contrôle de qualité d'usine : au lieu de découvrir les défauts après la production, on les détecte pendant la fabrication. Cela vous économise du temps et des frustrations.
TypeScript compile vers JavaScript pur, donc votre navigateur et vos serveurs exécutent du vrai JavaScript - TypeScript est juste une couche de sécurité ajoutée lors du développement.
Bloc de code :
// JavaScript sans sécurité de type - ATTENTION aux bugs !
let age = "25";
let result = age + 5; // Résultat : "255" (pas 30 !)
// TypeScript avec sécurité de type
let age: number = 25;
let result: number = age + 5; // Résultat : 30 ✓
// let age: number = "25"; // ❌ ERREUR détectée avant l'exécution !
Tableau comparatif :
| Aspect | JavaScript | TypeScript |
|---|---|---|
| Vérification des types | À l'exécution | Avant compilation |
| Courbe d'apprentissage | Facile | Modérée |
| Détection de bugs | Tardive | Précoce |
| Temps de débogage | Long | Court |
| Utilisation en production | Oui | Oui (compilé) |
Astuce : Installez TypeScript globalement avec npm install -g typescript. Vous pouvez alors compiler rapidement vos fichiers .ts en JavaScript avec la commande tsc nomfichier.ts.
Attention ⚠️ : TypeScript n'exécute pas de "magie" - il compile simplement vers JavaScript. Les erreurs TypeScript sont détectées uniquement pendant le développement, pas au runtime. Vous devez toujours exécuter votre code pour tester sa logique.
2. Les types de base en TypeScript
Définition : Les types de base sont les types de données fondamentaux que vous utiliserez constamment : string (texte), number (nombre), boolean (vrai/faux), et void (absence de valeur).
Explication détaillée :
TypeScript offre plusieurs types primitifs de base qui correspondent aux types JavaScript courants, mais avec des vérifications strictes. Le type string représente du texte ("bonjour"), number représente tous les nombres (5, 3.14, -10), boolean est soit true soit false, et void signifie "aucune valeur" (utilisé pour les fonctions qui ne retournent rien).
Imaginez les types comme des étiquettes sur les boîtes : une boîte étiquetée "string" ne peut contenir que du texte, jamais un nombre. Cela prévient les erreurs graves comme essayer d'appeler une méthode de texte sur un nombre.
Le type any est le joker dangereux - il désactive complètement la vérification de type. L'utiliser revient à revenir à JavaScript pur, donc évitez-le autant que possible.
Bloc de code :
// Types de base
let nom: string = "Alice";
let age: number = 28;
let actif: boolean = true;
let absence: void = undefined; // undefined uniquement
// Erreurs détectées par TypeScript
// nom = 42; // ❌ ERREUR : number n'est pas assignable à string
// age = "vingt-huit"; // ❌ ERREUR : string n'est pas assignable à number
// Type any - ⚠️ À ÉVITER
let flexible: any = "texte";
flexible = 42; // ✓ Autorisé mais dangereux !
// Inférence de type (TypeScript devine le type)
let ville = "Paris"; // TypeScript sait que c'est string
// ville = 123; // ❌ ERREUR : TypeScript a deviné string
Tableau des types de base :
| Type | Exemple | Description |
|---|---|---|
| string | "Hello", 'World' | Chaîne de caractères |
| number | 42, 3.14, -5 | Tout nombre (entier ou décimal) |
| boolean | true, false | Valeur logique vrai/faux |
| void | undefined | Aucune valeur (fonctions) |
| null | null | Absence intentionnelle de valeur |
| undefined | undefined | Valeur non définie |
| any | n'importe quoi | Désactive la vérification (dangereux) |
Astuce : Utilisez l'inférence de type ! Si vous écrivez let x = 5;, TypeScript sait automatiquement que x est un number. Vous n'avez pas besoin de taper : number. Cela rend votre code plus lisible tout en gardant la sécurité.
Attention ⚠️ : Le type any est tentant quand vous êtes bloqué, mais c'est une mauvaise pratique. Elle annule tous les bénéfices de TypeScript. Utilisez plutôt unknown (plus sûr) ou passez du temps à comprendre le vrai type.
3. Les fonctions et leurs signatures de type
Définition : Une signature de fonction en TypeScript spécifie les types des paramètres en entrée et le type de la valeur retournée. C'est un contrat qui garantit que la fonction reçoit et produit les bonnes données.
Explication détaillée :
Les fonctions sont le cœur du code JavaScript, et TypeScript les rend beaucoup plus sûres en vous forçant à déclarer exactement quels types de données elles acceptent et retournent.
Une signature de fonction ressemble à une recette : "Cette fonction reçoit un nombre et du texte, puis retourne un booléen." Si vous appelez cette fonction avec un texte et un nombre (ordre inversé), TypeScript vous arrête immédiatement.
Cela empêche des bugs subtils où le code compile mais produit des résultats étranges. C'est particulièrement utile dans les équipes, où la signature de fonction devient de la documentation exécutable.
Bloc de code :
// Signature complète : (paramètres: types) => typeRetour
function additionner(a: number, b: number): number {
return a + b;
}
const resultat = additionner(5, 3); // ✓ Correct
// additionner("5", 3); // ❌ ERREUR : string n'est pas number
// Fonction sans retour (void)
function afficherMessage(message: string): void {
console.log(message);
}
// Fonctions fléchées avec types
const multiplier = (x: number, y: number): number => {
return x * y;
};
// Paramètres optionnels (avec ?)
function saluer(prenom: string, nom?: string): string {
if (nom) {
return `Bonjour ${prenom} ${nom}`;
}
return `Bonjour ${prenom}`;
}
console.log(saluer("Marie")); // ✓ Correct
console.log(saluer("Marie", "Dupont")); // ✓ Correct
// Paramètres avec valeurs par défaut
function creerUtilisateur(nom: string, role: string = "user"): object {
return { nom, role };
}
Tableau des éléments de signature :
| Élément | Syntaxe | Exemple | Description |
|---|---|---|---|
| Paramètre typé | nom: type |
age: number |
Argument avec type spécifié |
| Paramètre optionnel | nom?: type |
email?: string |
Peut être omis lors de l'appel |
| Valeur par défaut | nom: type = valeur |
role: string = "user" |
Valeur utilisée si non fourni |
| Type retour | : type |
: boolean |
Type de la valeur retournée |
| Pas de retour | : void |
: void |
La fonction ne retourne rien |
Astuce : Utilisez les paramètres optionnels pour rendre vos fonctions flexibles sans perdre la sécurité de type. Combinez-les avec des valeurs par défaut pour avoir du code propre et expressif.
Attention ⚠️ : Oubliez le type de retour et TypeScript le devinera, mais c'est une mauvaise pratique. Toujours spécifier : type explicitement aide les autres développeurs (et vous dans 6 mois !) à comprendre votre fonction d'un coup d'œil.
4. Les objets et les interfaces
Définition : Une interface est un plan ou un schéma qui définit la structure d'un objet - quelles propriétés il doit avoir, leurs types, et lesquelles sont optionnelles. C'est comme une déclaration : "tout objet de ce type DOIT avoir ces propriétés".
Explication détaillée :
Les objets sont partout en JavaScript, mais sans types ils peuvent avoir n'importe quelles propriétés dans n'importe quel ordre. TypeScript les sécurise avec les interfaces.
Pensez à une interface comme à un formulaire d'inscription : elle liste les champs obligatoires (nom, email) et optionnels (numéro de téléphone). Quand quelqu'un remplit le formulaire (crée un objet), vous vérifiez qu'il a rempli tous les champs obligatoires avec le bon type.
Les interfaces n'existent que pendant le développement - elles disparaissent complètement quand TypeScript compile en JavaScript. Elles sont purement pour votre sécurité de développement.
Bloc de code :
// Définir une interface
interface Utilisateur {
nom: string;
email: string;
age: number;
premium?: boolean; // Optionnel (le ?)
}
// Créer un objet conforme à l'interface
const utilisateur1: Utilisateur = {
nom: "Alice",
email: "alice@example.com",
age: 28,
premium: true
};
// Cet objet convient aussi (sans propriété optionnelle)
const utilisateur2: Utilisateur = {
nom: "Bob",
email: "bob@example.com",
age: 35
};
// ❌ ERREUR : propriété manquante
// const utilisateur3: Utilisateur = {
// nom: "Charlie",
// email: "charlie@example.com"
// // Manque 'age' !
// };
// Interface pour les produits
interface Produit {
id: number;
titre: string;
prix: number;
description?: string;
stock: boolean;
}
function acheterProduit(produit: Produit): void {
console.log(`Achat de ${produit.titre} pour ${produit.prix}€`);
}
const laptop: Produit = {
id: 1,
titre: "Laptop Pro",
prix: 1200,
stock: true
};
acheterProduit(laptop); // ✓ Fonctionne parfaitement
Tableau des propriétés d'interface :
| Propriété | Syntaxe | Exemple | Obligatoire ? |
|---|---|---|---|
| Propriété basique | nom: type |
age: number |
Oui |
| Optionnelle | nom?: type |
phone?: string |
Non |
| Lecture seule | readonly nom: type |
readonly id: number |
Oui (non modifiable) |
| Type union | nom: type1 | type2 |
statut: "active" | "inactive" |
Oui (l'un des deux) |
Astuce : Créez des interfaces pour chaque type de données récurrent dans votre application. Cela documente votre code et vous économise des centaines de bugs. Un petit investissement maintenant = moins de débogage plus tard.
Attention ⚠️ : Toutes les propriétés sont obligatoires par défaut. Utilisez ? explicitement pour les propriétés optionnelles. Oublier cela est une source commune d'erreurs TypeScript.
5. Les types unions et le narrowing
Définition : Un type union permet à une variable d'avoir plusieurs types possibles (ex: string | number). Le narrowing est la technique pour déterminer le type exact d'une variable avant de l'utiliser.
Explication détaillée :
Parfois une fonction doit accepter soit un nombre, soit du texte. Les unions de types vous permettent d'être flexible tout en restant sûr. Le narrowing garantit que vous ne confondez pas les types quand vous utilisez la variable.
Imaginez une boîte postale qui peut contenir une lettre OU un colis. Quand vous ouvrez la boîte, vous devez d'abord vérifier ce qu'elle contient avant de la manipuler. De même, avec les unions, vous "vérifiez" le type avant d'utiliser la variable.
TypeScript offre plusieurs techniques de narrowing : vérifier avec typeof, utiliser des conditions if, ou créer des fonctions de garde. Plus vous sécurisez votre code avec le narrowing, plus vos bugs diminuent.
Bloc de code :
// Type union : peut être string OU number
function afficherID(id: string | number): void {
console.log(`ID: ${id}`);
}
afficherID(42); // ✓ Correct
afficherID("ABC123"); // ✓ Correct
// afficherID(true); // ❌ ERREUR : boolean n'est pas dans l'union
// Narrowing avec typeof
function traiterDonnee(valeur: string | number): void {
if (typeof valeur === "string") {
// Inside this block, valeur est string
console.log(valeur.toUpperCase());
} else {
// Ici, valeur est number (l'autre option de l'union)
console.log(valeur.toFixed(2));
}
}
traiterDonnee("bonjour"); // BONJOUR
traiterDonnee(3.14159); // 3.14
// Union avec des littéraux (valeurs spécifiques)
type Statut = "actif" | "inactif" | "suspendu";
function definirStatut(utilisateur: string, statut: Statut): void {
console.log(`${utilisateur} est ${statut}`);
}
definirStatut("Alice", "actif"); // ✓
// definirStatut("Bob", "banni"); // ❌ ERREUR : pas dans l'union
// Narrowing avec des conditions
function traitementFlexible(entree: string | number | boolean): string {
if (typeof entree === "boolean") {
return entree ? "Oui" : "Non";
}
if (typeof entree === "number") {
return entree > 0 ? "Positif" : "Négatif";
}
// À ce point, TypeScript sait que c'est string
return entree.length > 0 ? "Texte long" : "Texte vide";
}
Tableau des techniques de narrowing :
| Technique | Syntaxe | Utilisation | Exemple |
|---|---|---|---|
| typeof | if (typeof x === "type") |
Types primitifs | typeof x === "string" |
| instanceof | if (x instanceof Class) |
Classes et objets | if (x instanceof Date) |
| Vérification truthiness | if (x) |
Null et undefined | if (email) |
| Égalité | if (x === valeur) |
Valeurs exactes | if (statut === "actif") |
| Type guards | function is...() |
Custom logic | function isString(x: any): x is string |
Astuce : Utilisez les types littéraux ("actif" | "inactif") pour les états fixes. TypeScript vous proposera automatiquement les valeurs valides, réduisant les typos. C'est puissant pour les énumérations sans code supplémentaire.
Attention ⚠️ : Toujours faire le narrowing avant d'utiliser des méthodes spécifiques à un type. Par exemple, .toUpperCase() n'existe que sur les strings - si vous l'appelez sur une variable string | number sans narrowing, TypeScript lèvera une erreur. C'est voulu et vous protège !