Cours — les bases, avec des exemples

TypeScript

Tous les projets de cette stack sont écrits en TypeScript (le --typescript de create-next-app, les fichiers .tsx). Ce chapitre t'apprend à le lire et à l'écrire, en partant de zéro.

Sommaire
01Pourquoi TypeScript ?
02Types de base
03Typer une fonction
04Objets & interfaces
05Union & types littéraux
06Optionnel, null & undefined
07Generics — les bases
08TypeScript avec React
RĂšgle du jeu : chaque chapitre tient en une idĂ©e + un exemple. Teste chaque exemple toi-mĂȘme — soit sur le Playground officiel (aucune installation, ça marche direct dans le navigateur), soit dans n'importe quel fichier .ts/.tsx d'un projet créé avec un des guides de ce site.

Une seule rĂšgle Ă  retenir avant de commencer : TypeScript, c'est du JavaScript auquel on ajoute des annotations de type. Si tu enlevais toutes les annotations (: string, : number...), le code redeviendrait du JavaScript classique et fonctionnerait pareil. TypeScript ne change rien Ă  l'exĂ©cution — il vĂ©rifie juste, avant que ça tourne, que tu ne te trompes pas de type quelque part.
📖 Tu veux d'abord la dĂ©finition en une phrase de TypeScript, Next.js, Tailwind... ? Le lexique complet est sur la page d'accueil →
01

Pourquoi TypeScript ?

Comprendre le problÚme que ça résout, avant d'apprendre la syntaxe.

  1. Regarde ce qui plante en JavaScript pur
    // JavaScript classique — ce code est acceptĂ© sans broncher
    function calculerPrix(prix, quantite) {
      return prix * quantite;
    }
    
    calculerPrix("dix", 3);  // "dix" * 3 → NaN, et personne n'est prĂ©venu
    En JavaScript, rien ne t'empĂȘche d'appeler calculerPrix avec une chaĂźne de caractĂšres Ă  la place d'un nombre. L'erreur (NaN, « Not a Number ») n'apparaĂźt que quand le code tourne — parfois chez un vrai utilisateur, des heures aprĂšs que tu as Ă©crit la fonction.
  2. Le mĂȘme code, en TypeScript
    // TypeScript — les ": type" sont les annotations
    function calculerPrix(prix: number, quantite: number) {
      return prix * quantite;
    }
    
    calculerPrix("dix", 3);  // erreur IMMÉDIATE, avant mĂȘme d'exĂ©cuter le code
    Le : number aprĂšs chaque paramĂštre dit Ă  TypeScript « ceci doit toujours ĂȘtre un nombre ». Si tu te trompes, ton Ă©diteur (VS Code, etc.) souligne la ligne en rouge tout de suite — tu corriges avant mĂȘme d'enregistrer, pas aprĂšs un bug en production.
    👁 Essaie toi-mĂȘme : colle les deux blocs sur le Playground. Le premier (JS) ne montre aucune erreur. Le second souligne "dix" en rouge avec le message Argument of type 'string' is not assignable to parameter of type 'number'.
  3. Retiens l'idée en une phrase
    TypeScript n'exĂ©cute jamais de code diffĂ©remment de JavaScript — il se contente de vĂ©rifier les types avant l'exĂ©cution et de refuser de compiler si tu t'es trompĂ©. C'est un correcteur orthographique pour ton code, pas un nouveau langage Ă  part entiĂšre.
Vérification
02

Types de base

Les quatre types que tu utiliseras dans 90% de ton code.

Prérequis : étape 1 lue.

  1. Texte, nombre, booléen
    let nom: string = "Samba";
    let age: number = 32;
    let estConnecte: boolean = true;
    La syntaxe est toujours la mĂȘme : nom-de-variable: type = valeur. Trois types couvrent la grande majoritĂ© des cas : string (texte, entre guillemets), number (tout nombre, entier ou dĂ©cimal — pas de type sĂ©parĂ© pour les dĂ©cimaux), boolean (seulement true ou false).
    👁 Essaie toi-mĂȘme : Ă©cris age = "trente-deux"; juste en dessous. TypeScript souligne la ligne : Type 'string' is not assignable to type 'number'.
  2. Un tableau
    let notes: number[] = [12, 15, 8, 17];
    let prenoms: string[] = ["Awa", "Moussa", "Fatou"];
    type[] veut dire « un tableau qui ne contient que ce type ». notes.push("bien") serait refusĂ© — un tableau de number[] ne peut accueillir que des nombres.
  3. Laisse TypeScript deviner tout seul, quand c'est évident
    let ville = "Paris";  // pas besoin d'écrire ": string", TypeScript le devine
    ville = 75;            // erreur quand mĂȘme — le type est "verrouillĂ©" dĂšs l'affectation
    C'est l'inférence de type : si tu donnes une valeur dÚs la déclaration, TypeScript comprend le type tout seul. Tu n'as besoin d'écrire : string explicitement que quand la valeur n'est pas encore connue (ex. un paramÚtre de fonction, vu au chapitre suivant).
    👁 Essaie toi-mĂȘme : survole ville avec ta souris dans VS Code (ou le Playground) — une infobulle affiche let ville: string, mĂȘme si tu ne l'as jamais Ă©crit.
  4. Le piÚge à éviter : any
    let donnee: any = "texte";
    donnee = 42;        // accepté
    donnee = true;      // acceptĂ© aussi — plus aucune vĂ©rification
    any dit Ă  TypeScript « n'importe quel type, arrĂȘte de vĂ©rifier ». Ça compile toujours, mais tu perds tout l'intĂ©rĂȘt de TypeScript. Évite any — si tu ne connais pas encore le type exact, prĂ©fĂšre unknown (mĂȘme idĂ©e, mais TypeScript t'oblige Ă  vĂ©rifier le type avant de t'en servir).
Vérification
03

Typer une fonction

Sécuriser ce qui entre (les paramÚtres) et ce qui sort (la valeur retournée) d'une fonction.

Prérequis : étape 2 validée.

  1. Type des paramĂštres et du retour
    function additionner(a: number, b: number): number {
      return a + b;
    }
    Chaque paramÚtre est typé individuellement (a: number, b: number). Le : number juste avant l'accolade { type ce que la fonction return. Si le corps de la fonction retourne autre chose qu'un number, TypeScript refuse.
    👁 Essaie toi-mĂȘme : remplace return a + b; par return "rĂ©sultat: " + (a + b); — TypeScript souligne cette ligne, car une chaĂźne ne correspond pas au : number promis.
  2. Une fonction qui ne retourne rien
    function afficherMessage(texte: string): void {
      console.log(texte);
    }
    void veut dire « cette fonction ne retourne aucune valeur exploitable » — utilisĂ© pour les fonctions qui font une action (afficher, envoyer un email...) sans return.
  3. Fonction fléchée (la forme la plus courante en React)
    const multiplier = (a: number, b: number): number => a * b;
    MĂȘme principe, Ă©criture diffĂ©rente — c'est la syntaxe que tu croiseras le plus dans les composants React de tes guides (ex. const handleClick = () => { ... }).
    👁 Essaie toi-mĂȘme : appelle multiplier(4, "deux") — l'erreur pointe directement le deuxiĂšme argument, avant mĂȘme que la fonction s'exĂ©cute.
Vérification
04

Objets & interfaces

DĂ©crire la forme exacte d'un objet — la brique la plus utilisĂ©e dans un vrai projet (un utilisateur, un produit, une commande...).

Prérequis : étape 3 validée.

  1. Type inline sur un objet
    let utilisateur: { nom: string; age: number } = {
      nom: "Aziz",
      age: 32,
    };
    Fonctionne, mais devient illisible dĂšs que l'objet grossit ou que tu dois le rĂ©utiliser ailleurs (dans une autre fonction, un autre fichier). D'oĂč l'interface ci-dessous.
  2. La mĂȘme chose avec une interface — la façon propre de faire
    interface Utilisateur {
      nom: string;
      age: number;
    }
    
    const utilisateur: Utilisateur = {
      nom: "Aziz",
      age: 32,
    };
    Une interface nomme une forme d'objet une seule fois, réutilisable partout (function afficher(u: Utilisateur) { ... }). C'est l'équivalent TypeScript d'un modÚle Prisma ou d'un schéma de base de données, mais cÎté code.
    👁 Essaie toi-mĂȘme : retire age: 32, de l'objet — TypeScript refuse avec Property 'age' is missing. Ajoute une propriĂ©tĂ© ville: "Paris" non prĂ©vue par l'interface — refusĂ© aussi (Object literal may only specify known properties).
  3. Réutilise l'interface dans une fonction
    function saluer(u: Utilisateur): string {
      return `Bonjour ${u.nom}, tu as ${u.age} ans.`;
    }
    C'est exactement ce que tu Ă©criras pour typer les props d'un composant React (Ă©tape 8) ou le retour d'une requĂȘte Prisma (prisma.utilisateur.findMany() renvoie un tableau de ce type).
Vérification
05

Union & types littéraux

Limiter une valeur Ă  un ensemble prĂ©cis de possibilitĂ©s — trĂšs utilisĂ© pour un statut, un rĂŽle, une catĂ©gorie.

Prérequis : étape 4 validée.

  1. Une variable qui accepte plusieurs types
    let identifiant: string | number;
    identifiant = "abc123";  // accepté
    identifiant = 42;        // accepté aussi
    identifiant = true;      // refusé
    Le | (« ou ») crĂ©e une union : la variable peut ĂȘtre l'un OU l'autre des types listĂ©s, jamais un troisiĂšme.
  2. Le cas le plus utile : limiter à des valeurs exactes (types littéraux)
    let statut: "en_attente" | "payee" | "annulee";
    statut = "payee";     // accepté
    statut = "en cours";  // refusĂ© — cette chaĂźne exacte n'est pas dans la liste
    Au lieu d'un string générique (qui accepterait n'importe quelle faute de frappe comme "payé" ou "Payee"), tu listes précisément les seules valeurs valides. C'est exactement ce que fait un enum Prisma dans schema.prisma, cÎté TypeScript.
    👁 Essaie toi-mĂȘme : Ă©cris statut = "Payee"; (majuscule) — refusĂ©, car ce n'est pas exactement une des trois chaĂźnes autorisĂ©es.
  3. Combiné dans une interface
    interface Commande {
      id: string;
      statut: "en_attente" | "payee" | "annulee";
    }
    C'est la mĂȘme logique que la Server Action createProduit ou createQueue de tes autres guides — le champ statut ne peut jamais contenir une valeur inattendue, ni dans le code, ni via une faute de frappe.
Vérification
06

Optionnel, null & undefined

GĂ©rer proprement les valeurs qui peuvent manquer — la source n°1 de bugs en JavaScript (« Cannot read property of undefined »).

Prérequis : étape 5 validée.

  1. Une propriété facultative
    interface Produit {
      nom: string;
      description?: string;  // le "?" veut dire : peut ĂȘtre absente
    }
    
    const p1: Produit = { nom: "Chaise" };                              // accepté, pas de description
    const p2: Produit = { nom: "Table", description: "En bois massif" }; // accepté aussi
    Sans le ?, TypeScript exigerait description sur chaque objet. Avec, c'est facultatif — mais TypeScript te force alors Ă  vĂ©rifier qu'elle existe avant de l'utiliser (Ă©tape suivante).
  2. Vérifier avant d'utiliser une valeur optionnelle
    function afficherDescription(p: Produit) {
      console.log(p.description.toUpperCase());  // erreur : peut ĂȘtre undefined
    }
    
    function afficherDescriptionCorrige(p: Produit) {
      if (p.description) {
        console.log(p.description.toUpperCase());  // accepté : TS sait qu'elle existe ici
      }
    }
    TypeScript refuse d'appeler une mĂ©thode (.toUpperCase()) sur une valeur qui pourrait ĂȘtre undefined — c'est exactement le bug JavaScript classique Cannot read properties of undefined, mais dĂ©tectĂ© avant l'exĂ©cution plutĂŽt que chez l'utilisateur.
    👁 Essaie toi-mĂȘme : colle la premiĂšre fonction (sans le if) — TypeScript souligne p.description avec 'p.description' is possibly 'undefined'.
  3. Le raccourci ?. (optional chaining)
    console.log(p.description?.toUpperCase());  // si "description" n'existe pas, retourne undefined au lieu de planter
    Équivalent plus court du if prĂ©cĂ©dent, trĂšs courant dans le code React de tes guides (ex. session?.user.id pour l'authentification, dĂ©jĂ  vu dans les guides backoffice/e-commerce/SaaS).
Vérification
07

Generics — les bases

Écrire une fonction qui marche avec n'importe quel type, sans perdre la sĂ©curitĂ© des types. La notion la plus abstraite du cours — vas-y doucement.

Prérequis : étape 6 validée.

  1. Le problÚme que les generics résolvent
    function premierElement(tableau: any[]) {
      return tableau[0];
    }
    
    const x = premierElement([1, 2, 3]);        // x est "any" — TS a perdu la trace du type
    const y = premierElement(["a", "b", "c"]);  // y est "any" aussi, alors que c'est forcément une string
    Avec any[], la fonction accepte tout, mais TypeScript ne peut plus te dire ce que x ou y contiennent rĂ©ellement — tu as perdu toute la sĂ©curitĂ© de typage Ă  l'intĂ©rieur de la fonction.
  2. La mĂȘme fonction, avec un generic <T>
    function premierElement<T>(tableau: T[]): T {
      return tableau[0];
    }
    
    const x = premierElement([1, 2, 3]);        // x est bien typé "number"
    const y = premierElement(["a", "b", "c"]);  // y est bien typé "string"
    T est un « type variable » : un espace rĂ©servĂ© que TypeScript remplace automatiquement par le vrai type Ă  chaque appel — number pour le premier appel, string pour le second. Tu peux l'appeler T (convention) ou n'importe quel nom, comme un paramĂštre normal mais pour un type.
    👁 Essaie toi-mĂȘme : survole x puis y dans VS Code/Playground — les infobulles affichent const x: number et const y: string, TypeScript a devinĂ© tout seul.
  3. LĂ  oĂč tu en verras le plus souvent : useState en React
    const [age, setAge] = useState<number>(0);
    // setAge("trente") serait refusé : age doit toujours rester un "number"
    Tu n'as pas besoin d'Ă©crire tes propres generics au quotidien — mais tu vas constamment en utiliser (useState<T>, Array<T>, les hooks de tes composants React) donc reconnaĂźtre la syntaxe <...> et savoir qu'elle « fixe » un type suffit largement pour l'instant.
Vérification
08

TypeScript avec React

Tout assembler sur un composant réel, comme ceux que tu écris dans les guides vitrine/backoffice/e-commerce/SaaS/mobile.

Prérequis : étapes 1 à 7 validées.

  1. Typer les props d'un composant
    // components/carte-produit.tsx
    interface CarteProduitProps {
      nom: string;
      prix: number;
      enPromo?: boolean;  // optionnel, vu à l'étape 6
    }
    
    export default function CarteProduit({ nom, prix, enPromo }: CarteProduitProps) {
      return (
        <div>
          <h3>{nom}</h3>
          <p>{prix} €{enPromo ? " — en promo" : ""}</p>
        </div>
      );
    }
    C'est l'interface de l'Ă©tape 4, appliquĂ©e aux props d'un composant. Si quelqu'un (toi, dans trois mois) utilise <CarteProduit nom="Chaise" /> sans prix, TypeScript refuse — impossible d'oublier une prop obligatoire, contrairement au JavaScript classique.
    👁 Essaie toi-mĂȘme : dans un fichier qui importe ce composant, Ă©cris <CarteProduit nom="Chaise" prix="20" /> (avec des guillemets autour de 20) — erreur immĂ©diate, prix attend un number, pas une string.
  2. Typer un état avec useState
    "use client";
    import { useState } from "react";
    
    export default function Compteur() {
      const [total, setTotal] = useState<number>(0);
    
      return (
        <button onClick={() => setTotal(total + 1)}>
          Cliqué {total} fois
        </button>
      );
    }
    Le generic (Ă©tape 7) sur useState<number> garantit que total reste un nombre pendant toute la vie du composant — setTotal("beaucoup") serait refusĂ©.
  3. Typer les données qui viennent de la base (Prisma)
    import { prisma } from "@/lib/db/client";
    
    export default async function ListeProduits() {
      const produits = await prisma.produit.findMany();
      // "produits" est dĂ©jĂ  typĂ© automatiquement par Prisma — pas d'interface Ă  Ă©crire Ă  la main
    
      return (
        <ul>
          {produits.map((produit) => (
            <li key={produit.id}>{produit.nom}</li>
          ))}
        </ul>
      );
    }
    Bonne nouvelle pour la suite : Prisma gĂ©nĂšre lui-mĂȘme les types TypeScript Ă  partir de ton schema.prisma (vu dans les guides backoffice/e-commerce/SaaS). Tu n'as pas besoin de réécrire une interface pour chaque modĂšle — produit.nom est dĂ©jĂ  typĂ© string, produit.prix dĂ©jĂ  typĂ© number, automatiquement.
Vérification