Note de Référence Complète sur TypeScript
Table des Matières
- Introduction à TypeScript
- Syntaxe de Base et Structure d’un Programme
- Le Système de Types : Primitifs, Composés et Annotations
- Interfaces, Types Personnalisés, Unions et Intersections
- Classes et Programmation Orientée Objet (POO)
- Fonctions, Paramètres, Génériques et Types Utilitaires
- Gestion des Modules : Import et Export
- Compatibilité et Intégration avec JavaScript
- Configuration Stricte et Options du Compilateur
- Concepts Avancés : Décorateurs et Métaprogrammation
- Meilleures Pratiques et Conventions de Codage
1. Introduction à TypeScript
TypeScript est un sur-ensemble (superset) de JavaScript, développé et maintenu par Microsoft. Il ajoute un typage statique optionnel et d’autres fonctionnalités orientées objet au langage JavaScript. Le principal avantage de TypeScript est de permettre la détection d’erreurs de type pendant la phase de développement (compilation) plutôt qu’à l’exécution, ce qui rend le code plus robuste, prévisible et facile à maintenir, en particulier dans les projets de grande envergure.
Le code TypeScript est transpilé en code JavaScript standard, qui peut ensuite être exécuté par n’importe quel navigateur ou environnement JavaScript (comme Node.js).
2. Syntaxe de Base et Structure d’un Programme
Tout code JavaScript valide est également du code TypeScript valide. La principale différence syntaxique est l’ajout d’annotations de type.
Annotation de Type
On utilise les deux-points (:) suivis du nom du type pour annoter une variable, un paramètre de fonction ou une valeur de retour.
// Déclaration d'une variable avec un type
let message: string = "Bonjour, TypeScript !";
let annee: number = 2023;
let estActif: boolean = true;
// Structure d'une fonction simple
function saluer(nom: string): void {
console.log(`Bonjour, ${nom} !`);
}
saluer(message);Compilation
Le code TypeScript (fichier .ts) doit être compilé en JavaScript (fichier .js) à l’aide du compilateur TypeScript (tsc).
- Installation :
npm install -g typescript - Compilation :
tsc mon_fichier.ts(génèremon_fichier.js)
3. Le Système de Types : Primitifs, Composés et Annotations
TypeScript enrichit le système de types de JavaScript.
Types Primitifs
string: Chaînes de caractères ("hello",'world',`template`).number: Nombres (entiers et flottants :10,3.14).boolean: Booléens (true,false).null: Représente une absence intentionnelle de valeur.undefined: Représente une variable qui n’a pas encore été assignée.symbol: Identifiant unique et immuable (introduit en ES6).bigint: Pour les très grands entiers.
Types Composés
-
Tableaux (
Array) : Il existe deux syntaxes.let nombres: number[] = [1, 2, 3]; let noms: Array<string> = ["Alice", "Bob"]; -
Tuples : Un tableau avec une longueur et des types de données fixes pour chaque élément.
let utilisateur: [string, number]; utilisateur = ["Alice", 25]; // OK // utilisateur = [25, "Alice"]; // Erreur -
Énumérations (
Enum) : Pour créer un ensemble de constantes nommées.enum Couleur { Rouge, Vert, Bleu } let c: Couleur = Couleur.Vert; // c vaut 1 par défaut enum Statut { Pending = "PENDING", Approved = "APPROVED", Rejected = "REJECTED" } let s: Statut = Statut.Approved; // s vaut "APPROVED"
Types Spéciaux
any: Désactive la vérification de type. À utiliser avec parcimonie, car il annule les avantages de TypeScript.let variableMagique: any = 4; variableMagique = "Je suis maintenant une chaîne"; // OKunknown: Une alternative plus sûre àany. Vous devez effectuer une vérification de type avant d’utiliser une variable de typeunknown.let valeurInconnue: unknown = "Ceci pourrait être n'importe quoi"; if (typeof valeurInconnue === "string") { console.log(valeurInconnue.toUpperCase()); // OK après vérification }void: Indique l’absence de valeur de retour pour une fonction.never: Indique qu’une fonction ne se termine jamais normalement (par exemple, elle lève toujours une exception ou contient une boucle infinie).
4. Interfaces, Types Personnalisés, Unions et Intersections
Ces constructions permettent de définir des structures de données complexes et flexibles.
Interfaces
Une interface est un contrat qui définit la forme (les propriétés et méthodes) d’un objet.
interface Utilisateur {
id: number;
nom: string;
email?: string; // Propriété optionnelle avec '?'
readonly dateCreation: Date; // Propriété en lecture seule
direBonjour(): string;
}
let user: Utilisateur = {
id: 1,
nom: "John Doe",
dateCreation: new Date(),
direBonjour: () => `Bonjour, je suis ${user.nom}`
};Types Personnalisés (type)
L’alias de type permet de donner un nom à n’importe quel type, qu’il soit primitif, objet, union, etc.
type ID = string | number;
type Point = {
x: number;
y: number;
};
let userId: ID = "abc-123";
let p1: Point = { x: 10, y: 20 };Interface vs Type : Les interfaces sont “ouvertes” (on peut les étendre par fusion de déclarations), tandis que les types sont “fermés”. Utilisez les interfaces pour définir des formes d’objets ou des contrats de classes, et les types pour des unions, intersections ou des types plus complexes.
Types Union (|)
Un type union permet à une variable d’accepter plusieurs types différents.
function afficherId(id: string | number) {
if (typeof id === "string") {
console.log(id.toUpperCase()); // Traitement spécifique au string
} else {
console.log(id); // Traitement spécifique au number
}
}Types Intersection (&)
Un type intersection combine plusieurs types en un seul, qui possède toutes les propriétés des types combinés.
interface Personne {
nom: string;
}
interface Employe {
codeEmploye: number;
}
type PersonneEmploye = Personne & Employe;
let employe: PersonneEmploye = {
nom: "Jane Doe",
codeEmploye: 123
};5. Classes et Programmation Orientée Objet (POO)
TypeScript prend en charge la POO avec des classes, de l’héritage, des modificateurs d’accès et des interfaces.
Classes et Constructeur
class Animal {
// Modificateurs d'accès: public (défaut), private, protected
private nom: string;
constructor(nom: string) {
this.nom = nom;
}
public seDeplacer(distance: number = 0): void {
console.log(`${this.nom} s'est déplacé de ${distance}m.`);
}
}Héritage (extends)
class Chien extends Animal {
constructor(nom: string) {
super(nom); // Appelle le constructeur de la classe parente
}
aboyer(): void {
console.log("Wouaf !");
}
}
const monChien = new Chien("Rex");
monChien.seDeplacer(10);
monChien.aboyer();Implémentation d’Interfaces (implements)
Une classe peut implémenter une interface pour garantir qu’elle respecte un contrat spécifique.
interface Loggable {
log(): void;
}
class Produit implements Loggable {
constructor(public nom: string, public prix: number) {}
log(): void {
console.log(`Produit: ${this.nom}, Prix: ${this.prix}€`);
}
}Classes Abstraites
Les classes abstraites ne peuvent pas être instanciées directement et peuvent contenir des membres abstraits que les classes filles doivent implémenter.
abstract class Figure {
abstract calculerAire(): number;
afficherAire(): void {
console.log("L'aire est : " + this.calculerAire());
}
}
class Cercle extends Figure {
constructor(public rayon: number) {
super();
}
calculerAire(): number {
return Math.PI * this.rayon ** 2;
}
}6. Fonctions, Paramètres, Génériques et Types Utilitaires
Typage des Fonctions
// Annotation des paramètres et de la valeur de retour
function additionner(a: number, b: number): number {
return a + b;
}
// Type pour une fonction
let maFonction: (x: number, y: number) => number;
maFonction = additionner;Paramètres de Fonctions
- Optionnels (
?) : Doivent être les derniers. - Par défaut : Attribue une valeur si non fournie.
- Rest (
...) : Regroupe les paramètres restants dans un tableau.
function construireNom(prenom: string, nom?: string, initiales: string = "N/A"): string {
return `${prenom} ${nom || ''} (${initiales})`;
}
function somme(...nombres: number[]): number {
return nombres.reduce((total, num) => total + num, 0);
}Génériques (Generics)
Les génériques permettent de créer des composants (fonctions, classes, interfaces) réutilisables qui peuvent fonctionner avec différents types tout en conservant la sécurité de type.
// Fonction générique
function premiereElement<T>(arr: T[]): T | undefined {
return arr[0];
}
let premierNombre = premiereElement([1, 2, 3]); // Type inféré : number
let premierString = premiereElement(["a", "b", "c"]); // Type inféré : string
// Interface générique
interface Conteneur<T> {
contenu: T;
}
let boiteDeChaine: Conteneur<string> = { contenu: "texte" };Types Utilitaires (Utility Types)
TypeScript fournit des types utilitaires pour manipuler les types existants.
Partial<T>: Rend toutes les propriétés deToptionnelles.Readonly<T>: Rend toutes les propriétés deTen lecture seule.Pick<T, K>: Crée un type en sélectionnant un ensemble de propriétésKdeT.Omit<T, K>: Crée un type en omettant un ensemble de propriétésKdeT.Record<K, T>: Crée un type d’objet dont les clés sont de typeKet les valeurs de typeT.
interface Todo {
titre: string;
description: string;
estFait: boolean;
}
// Rend toutes les propriétés de Todo optionnelles
function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
return { ...todo, ...fieldsToUpdate };
}
// Ne contient que 'titre' et 'estFait'
type TodoPreview = Pick<Todo, "titre" | "estFait">;7. Gestion des Modules : Import et Export
TypeScript utilise la syntaxe des modules ES6 pour organiser le code en fichiers réutilisables.
Exportation (export)
- Exportations Nommées : Exporter plusieurs éléments d’un module.
// Fichier: math.ts export const PI = 3.14; export function ajouter(a: number, b: number): number { return a + b; } - Exportation par Défaut (
export default) : Exporter une seule entité principale.// Fichier: MonComposant.ts export default class MonComposant { // ... }
Importation (import)
// Fichier: main.ts
// Importations nommées
import { PI, ajouter } from './math';
console.log(ajouter(PI, 2));
// Importation par défaut
import Composant from './MonComposant';
const instance = new Composant();8. Compatibilité et Intégration avec JavaScript
TypeScript est conçu pour une intégration transparente avec l’écosystème JavaScript.
- Adoption Graduelle : Vous pouvez renommer un fichier
.jsen.tset commencer à ajouter des types progressivement. - Fichiers de Déclaration (
.d.ts) : Pour utiliser des bibliothèques JavaScript existantes qui n’ont pas été écrites en TypeScript, on utilise des fichiers de déclaration de type. Ces fichiers décrivent les types et les signatures des fonctions de la bibliothèque. - DefinitelyTyped : La communauté maintient un immense référentiel de fichiers de déclaration pour des milliers de bibliothèques JS sous le scope
@typessur npm.# Exemple pour installer les types de Node.js et de React npm install --save-dev @types/node @types/react
9. Configuration Stricte et Options du Compilateur
Le comportement du compilateur TypeScript est contrôlé par le fichier tsconfig.json à la racine du projet.
Fichier tsconfig.json de base
{
"compilerOptions": {
"target": "ES2018", // Version JavaScript cible
"module": "commonjs", // Système de modules
"outDir": "./dist", // Dossier de sortie pour les fichiers .js
"rootDir": "./src", // Dossier source des fichiers .ts
"strict": true, // Active toutes les options de vérification stricte
"esModuleInterop": true // Améliore l'interopérabilité avec les modules CommonJS
},
"include": ["src/**/*"] // Fichiers à inclure dans la compilation
}Gestion Stricte des Erreurs (strict: true)
Activer strict: true est une meilleure pratique. Cela active un ensemble d’options qui renforcent la sécurité du type, notamment :
noImplicitAny: Lève une erreur sur les variables et paramètres avec un typeanyimplicite.strictNullChecks: Différencienulletundefineddes autres types. Vous devez explicitement vérifier leur présence.strictFunctionTypes: Vérifie la covariance des paramètres de fonction.noImplicitThis: Lève une erreur sur l’utilisation dethisavec un typeanyimplicite.
10. Concepts Avancés : Décorateurs et Métaprogrammation
Les décorateurs sont une fonctionnalité expérimentale qui permet d’annoter et de modifier des classes et leurs membres (méthodes, propriétés). Ils sont largement utilisés dans des frameworks comme Angular et NestJS.
Pour les utiliser, activez l’option experimentalDecorators dans tsconfig.json.
Exemple de Décorateur de Méthode
// Un décorateur est une fonction
function logExecutionTime(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
const start = performance.now();
const result = originalMethod.apply(this, args);
const end = performance.now();
console.log(`La méthode ${propertyKey} a mis ${end - start}ms à s'exécuter.`);
return result;
};
return descriptor;
}
class Calculateur {
@logExecutionTime
calculComplexe() {
// Simule une opération longue
for (let i = 0; i < 1e7; i++) {}
console.log("Calcul terminé.");
}
}
const calc = new Calculateur();
calc.calculComplexe();
// Affiche: "Calcul terminé."
// Affiche: "La méthode calculComplexe a mis Xms à s'exécuter."11. Meilleures Pratiques et Conventions de Codage
- Activer le Mode
strict: C’est la recommandation la plus importante pour tirer pleinement parti de TypeScript. - Éviter
any: N’utilisezanyqu’en dernier recours. Préférezunknownsi le type est réellement inconnu. - Utiliser l’Inférence de Type : Laissez TypeScript inférer les types autant que possible pour alléger le code.
let nom = "Alice"; // Inutile d'écrire let nom: string - Préférer
interfacepour les Formes d’Objets : Utilisez les interfaces pour définir des contrats pour les objets et les classes. - Utiliser
typepour les Unions et Intersections : Les alias de type sont plus adaptés pour combiner des types. - Utiliser
ESLint: Intégrezeslintavec le plugin@typescript-eslintpour maintenir une qualité de code cohérente et détecter les problèmes potentiels. - Organiser le Code en Modules : Structurez votre application en petits modules cohésifs et réutilisables.
- Tirer parti des
readonly: Utilisezreadonlypour les propriétés qui ne doivent pas être modifiées après leur initialisation, favorisant ainsi l’immutabilité.