Typescript fondamentaux
Thème :
Intermédiaire
1 vues ·1/14 leçons

01 - Introduction TypeScript : Du JavaScript Typé au Code Robuste

1.1 Qu’est-ce que TypeScript en 2025 ?

Définition et Philosophie

TypeScript est un sur-ensemble de JavaScript qui ajoute des types statiques au langage. Créé par Microsoft en 2012, il est devenu le standard de facto pour le développement JavaScript moderne.

// JavaScript classique
function calculerAge(nom, anneeNaissance) {
    const age = new Date().getFullYear() - anneeNaissance;
    return `${nom} a ${age} ans`;
}

// TypeScript avec types
function calculerAge(nom: string, anneeNaissance: number): string {
    const age: number = new Date().getFullYear() - anneeNaissance;
    return `${nom} a ${age} ans`;
}

Pourquoi TypeScript en 2025 ?

  • Sécurité du type : Erreurs détectées à la compilation
  • IntelliSense avancé : Auto-complétion contextuelle
  • Refactoring sûr : Renommage automatique et fiable
  • Documentation vivante : Les types servent de documentation
  • Adoption massive : 95%+ des projets JavaScript modernes

Écosystème TypeScript 2025

  • TypeScript 5.4+ : Dernières fonctionnalités
  • IDE support : VS Code, WebStorm, Vim
  • Build tools : Vite, esbuild, SWC
  • Framework integration : React, Vue, Angular, Svelte

1.2 Installation et Configuration

Installation Globale

# Installation avec npm
npm install -g typescript

# Installation avec pnpm
pnpm add -g typescript

# Installation avec yarn
yarn global add typescript

# Vérification de l'installation
tsc --version

Installation de Projet

# Projet TypeScript initial
npm init -y
npm install typescript --save-dev
npm install @types/node --save-dev

# Configuration TypeScript
npx tsc --init

tsconfig.json Essentiel

{
    "compilerOptions": {
        "target": "ES2024",
        "module": "ESNext",
        "moduleResolution": "node",
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true,
        "declaration": true,
        "outDir": "./dist",
        "rootDir": "./src",
        "sourceMap": true,
        "removeComments": false,
        "noImplicitAny": true,
        "strictNullChecks": true,
        "strictFunctionTypes": true,
        "noImplicitReturns": true,
        "noFallthroughCasesInSwitch": true,
        "noUncheckedIndexedAccess": true,
        "exactOptionalPropertyTypes": true
    },
    "include": ["src/**/*"],
    "exclude": ["node_modules", "dist"]
}

1.3 Types de Base

Types Primitifs

// Types de base
let nom: string = "Alice";
let age: number = 25;
let estActif: boolean = true;
let rien: null = null;
let nonDefini: undefined = undefined;
let unique: symbol = Symbol("id");

// Types spéciaux
let grandNombre: bigint = 9007199254740991n;
let donneesBinaires: any = { /* n'importe quoi */ };
let inconnu: unknown = /* valeur inconnue */;

// Tableaux typés
let nombres: number[] = [1, 2, 3, 4, 5];
let noms: Array<string> = ["Alice", "Bob", "Charlie"];

Types Spéciaux

// any : Désactive le typage (à éviter)
let flexible: any = "peut être n'importe quoi";

// unknown : Type sécurisé pour valeurs inconnues
let donnee: unknown = JSON.parse(jsonString);

// never : Type qui n'arrive jamais
function erreur(message: string): never {
    throw new Error(message);
}

// void : Absence de retour
function logger(message: string): void {
    console.log(message);
}

Tuple et Enum

// Tuple : Tableau avec types fixes
let coordonnees: [number, number] = [10, 20];
let personne: [string, number, boolean] = ['Alice', 25, true];

// Enum : Ensemble de constantes nommées
enum Statut {
    EnAttente = 'en_attente',
    EnCours = 'en_cours',
    Termine = 'termine',
}

let statutCommande: Statut = Statut.EnCours;

1.4 Interfaces et Types d’Objets

Définition d’Interface

interface Utilisateur {
    id: number;
    nom: string;
    email: string;
    age?: number; // Optionnel
    readonly dateCreation: Date; // Lecture seule
}

const utilisateur: Utilisateur = {
    id: 1,
    nom: 'Alice',
    email: 'alice@example.com',
    dateCreation: new Date(),
};

Types Union et Intersection

// Union : Un type parmi plusieurs
type ID = string | number;
let userId: ID = 'user123'; // ou 123

type Statut = 'en_attente' | 'en_cours' | 'termine';

// Intersection : Combinaison de types
interface Personne {
    nom: string;
    age: number;
}

interface Employe {
    salaire: number;
    poste: string;
}

type EmployePersonne = Personne & Employe;

const employe: EmployePersonne = {
    nom: 'Alice',
    age: 30,
    salaire: 50000,
    poste: 'Développeuse',
};

Types Avancés

// Type conditionnel
type NonNull<T> = T extends null ? never : T;
type Stringify<T> = {
    [K in keyof T]: string;
};

// Type mappé
type Partiel<T> = {
    [P in keyof T]?: T[P];
};

interface UtilisateurComplet {
    id: number;
    nom: string;
    email: string;
    age: number;
}

type UtilisateurPartiel = Partiel<UtilisateurComplet>;

1.5 Fonctions Typées

Typage des Fonctions

// Fonction fléchée typée
const addition = (a: number, b: number): number => a + b;

// Fonction classique typée
function multiplier(x: number, y: number): number {
    return x * y;
}

// Paramètres optionnels
function saluer(nom: string, titre?: string): string {
    if (titre) {
        return `Bonjour ${titre} ${nom}`;
    }
    return `Bonjour ${nom}`;
}

// Paramètres par défaut
function creerCompte(nom: string, email: string, role: string = 'utilisateur'): void {
    console.log(`Compte créé pour ${nom} (${email}) avec le rôle ${role}`);
}

Fonctions Génériques

// Fonction générique simple
function identite<T>(valeur: T): T {
    return valeur;
}

const numero = identite(42); // number
const texte = identite('hello'); // string

// Contraintes génériques
function premierElement<T>(tableau: T[]): T | undefined {
    return tableau.length > 0 ? tableau[0] : undefined;
}

// Utilitaires génériques
interface Repository<T> {
    trouver(id: number): T | null;
    sauvegarder(item: T): void;
    supprimer(id: number): boolean;
}

1.6 Classes en TypeScript

Classe de Base

class Animal {
    // Propriétés publiques
    public nom: string;
    protected age: number;
    private espece: string;

    // Constructeur typé
    constructor(nom: string, age: number, espece: string) {
        this.nom = nom;
        this.age = age;
        this.espece = espece;
    }

    // Méthode publique
    public presenter(): string {
        return `Je suis ${this.nom}, un ${this.espece} de ${this.age} ans`;
    }

    // Méthode protégée
    protected crier(): void {
        console.log(`${this.nom} fait du bruit !`);
    }
}

// Héritage
class Chien extends Animal {
    private race: string;

    constructor(nom: string, age: number, race: string) {
        super(nom, age, 'chien');
        this.race = race;
    }

    public aboyer(): void {
        console.log(`${this.nom} aboie !`);
        this.crier(); // Appel méthode protégée
    }

    // Redéfinition de méthode
    public presenter(): string {
        return `${super.presenter()} et je suis un ${this.race}`;
    }
}

Modificateurs d’Accès

class CompteBancaire {
    public titulaire: string;
    private solde: number;
    protected numeroCompte: string;
    readonly dateOuverture: Date;

    constructor(titulaire: string, depotInitial: number) {
        this.titulaire = titulaire;
        this.solde = depotInitial;
        this.numeroCompte = this.genererNumero();
        this.dateOuverture = new Date();
    }

    public deposer(montant: number): void {
        if (montant > 0) {
            this.solde += montant;
        }
    }

    private genererNumero(): string {
        return `FR${Math.random().toString(36).substr(2, 9).toUpperCase()}`;
    }

    protected verifierSolde(): boolean {
        return this.solde >= 0;
    }
}

1.7 Gestion des Erreurs

Types d’Erreurs Personnalisés

// Erreur personnalisée
class ErreurValidation extends Error {
    public code: string;
    public champ: string;

    constructor(message: string, code: string, champ: string) {
        super(message);
        this.name = 'ErreurValidation';
        this.code = code;
        this.champ = champ;
    }
}

// Utilisation des erreurs typées
function validerEmail(email: string): void {
    if (!email.includes('@')) {
        throw new ErreurValidation('Email invalide', 'EMAIL_INVALIDE', 'email');
    }
}

Union de Types d’Erreurs

type ErreurAPI = ErreurValidation | ErreurReseau | ErreurAuthentification;

class GestionnaireErreurs {
    public gererErreur(error: ErreurAPI): void {
        if (error instanceof ErreurValidation) {
            console.error(`Erreur de validation : ${error.champ}`);
        } else if (error instanceof ErreurReseau) {
            console.error(`Erreur réseau : ${error.message}`);
        }
    }
}

1.8 Utilitaires TypeScript

Type Guards

// Type guard function
function estString(valeur: unknown): valeur is string {
    return typeof valeur === 'string';
}

function estNombre(valeur: unknown): valeur is number {
    return typeof valeur === 'number';
}

// Utilisation avec type narrowing
function traiterValeur(valeur: unknown): void {
    if (estString(valeur)) {
        console.log(valeur.toUpperCase()); // TypeScript sait que c'est une string
    } else if (estNombre(valeur)) {
        console.log(valeur.toFixed(2)); // TypeScript sait que c'est un number
    }
}

Types Utilitaires

// Type utilitaire pour rendre toutes les propriétés optionnelles
type Partiel<T> = {
    [P in keyof T]?: T[P];
};

// Type utilitaire pour rendre les propriétés requises
type Requis<T> = {
    [P in keyof T]-?: T[P];
};

// Type utilitaire pour extraire les propriétés
type ProprietesSeulement<T> = {
    [K in keyof T]: T[K];
};

// Application
interface UtilisateurComplet {
    id: number;
    nom: string;
    email: string;
    age: number;
}

type UtilisateurCreation = Partiel<UtilisateurComplet>;
type UtilisateurLecture = ProprietesSeulement<UtilisateurComplet>;

🎯 TP 1 : Système de Gestion d’Utilisateurs avec TypeScript

Objectifs

  • Appliquer les concepts fondamentaux de TypeScript
  • Créer des interfaces et types robustes
  • Implémenter la validation de types
  • Utiliser les classes et l’héritage
  • Gérer les erreurs de manière typée

Fonctionnalités Requises

1. Modèle de Données

interface Utilisateur {
    id: number;
    nom: string;
    email: string;
    age: number;
    dateCreation: Date;
    derniereConnexion?: Date;
}

interface Compte {
    id: number;
    idUtilisateur: number;
    type: 'admin' | 'utilisateur' | 'moderateur';
    statut: 'actif' | 'suspendu' | 'ferme';
    dateCreation: Date;
}

2. Classes de Service

class ServiceUtilisateur {
    private utilisateurs: Utilisateur[] = [];
    private prochainId: number = 1;

    public creerUtilisateur(donnees: Omit<Utilisateur, 'id'>): Utilisateur {
        const utilisateur: Utilisateur = {
            id: this.prochainId++,
            ...donnees,
            dateCreation: new Date(),
        };

        this.utilisateurs.push(utilisateur);
        return utilisateur;
    }

    public trouverUtilisateur(id: number): Utilisateur | null {
        return this.utilisateurs.find((u) => u.id === id) || null;
    }

    public listerUtilisateurs(): Utilisateur[] {
        return [...this.utilisateurs];
    }
}

3. Validation de Types

class ValidateurUtilisateur {
    public static validerEmail(email: string): boolean {
        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        return emailRegex.test(email);
    }

    public static validerAge(age: number): boolean {
        return age >= 0 && age <= 150;
    }

    public static validerNom(nom: string): boolean {
        return nom.length >= 2 && nom.length <= 50;
    }
}

4. Gestion des Erreurs

class ErreurUtilisateur extends Error {
    constructor(
        message: string,
        public code: string,
        public champ?: string,
    ) {
        super(message);
        this.name = 'ErreurUtilisateur';
    }
}

class GestionnaireErreursUtilisateur {
    public static gerer(error: unknown): never {
        if (error instanceof ErreurUtilisateur) {
            console.error(`Erreur ${error.code}: ${error.message}`);
            throw error;
        }

        throw new ErreurUtilisateur('Erreur inconnue lors du traitement', 'ERREUR_INCONNUE');
    }
}

5. Interface Utilisateur

// Interface pour le formulaire de création
interface FormulaireCreationUtilisateur {
    nom: string;
    email: string;
    motDePasse: string;
    confirmationMotDePasse: string;
    age: string;
}

// Type pour les réponses API
interface ReponseAPI<T> {
    succes: boolean;
    donnees?: T;
    erreur?: {
        code: string;
        message: string;
        champ?: string;
    };
}

Instructions d’Implémentation

  1. Créer la structure du projet

    typescript-tp/
    ├── src/
    │   ├── models/
    │   │   ├── Utilisateur.ts
    │   │   └── Compte.ts
    │   ├── services/
    │   │   └── ServiceUtilisateur.ts
    │   ├── validators/
    │   │   └── ValidateurUtilisateur.ts
    │   ├── errors/
    │   │   └── ErreurUtilisateur.ts
    │   ├── types/
    │   │   └── index.ts
    │   └── index.ts
    ├── tsconfig.json
    └── package.json
    
  2. Implémenter les classes et interfaces

    • Créer les interfaces Utilisateur et Compte
    • Implémenter la classe ServiceUtilisateur avec méthodes CRUD
    • Ajouter la classe ValidateurUtilisateur avec validation de types
    • Créer la classe ErreurUtilisateur pour la gestion d’erreurs
  3. Ajouter les types utilitaires

    • Partiel<T> pour les formulaires
    • Requis<T> pour les validations
    • Types union pour les statuts et rôles
  4. Créer des tests

    • Tests unitaires pour chaque classe
    • Tests de validation de types
    • Tests de gestion d’erreurs
  5. Compiler et vérifier

    # Compilation
    npx tsc
    
    # Watch mode
    npx tsc --watch
    
    # Vérification des types
    npx tsc --noEmit
    

Critères de Réussite

  • ✅ Typage strict : Aucune utilisation de any
  • ✅ Interfaces complètes : Toutes les propriétés typées
  • ✅ Validation robuste : Vérification des types à l’exécution
  • ✅ Gestion d’erreurs : Types d’erreurs personnalisées
  • ✅ Code compilé : Zéro erreur de compilation TypeScript
  • ✅ Tests passants : Couverture minimale de 80%

Extensions Possibles

  1. Persistance : Sauvegarde des utilisateurs en localStorage
  2. Authentification : Système de login avec JWT
  3. Permissions : Rôles et droits d’accès
  4. API REST : Endpoints pour la gestion des utilisateurs
  5. Interface React : Composants React typés

📚 Ressources Complémentaires

Documentation Officielle

Outils Recommandés

  • VS Code : Meilleur support TypeScript
  • TypeScript Playground : Essai en ligne
  • DefinitelyTyped : Types pour les librairies JavaScript

Bonnes Pratiques

  • Toujours activer le mode strict
  • Préférer les interfaces aux types
  • Utiliser les types guards pour la sécurité
  • Documenter les types complexes
  • Éviter any autant que possible

Cette leçon pose les fondations de TypeScript en se basant sur les concepts JavaScript déjà maîtrisés, en ajoutant la sécurité du typage statique.

Prêt à passer à la suite ?

Vous avez terminé cette leçon sur 01 - Introduction TypeScript : Du JavaScript Typé au Code Robuste. Continuez votre apprentissage avec la prochaine leçon ou explorez d'autres formations.