Cours JavaScript et TypeScript · Module 6: Bases de TypeScript

Configuration et outils de TypeScript

5 min de lecture
Dans cette leçon7

À Jurassic Park, même le meilleur système génétique ne sert à rien sans un calibrage adéquat des équipements de laboratoire. De la même façon, TypeScript sans une configuration adaptée ne révèlera jamais tout son potentiel. Le fichier tsconfig.json est ton panneau de contrôle : il définit comment le compilateur TypeScript interprète et transforme ton code.

tsconfig.json - le centre de commande

Chaque projet TypeScript commence avec un fichier tsconfig.json à la racine. C'est un fichier JSON qui définit les options du compilateur, les fichiers à compiler, et à quoi doit ressembler le résultat :

1{
2  "compilerOptions": {
3    "target": "ES2020",
4    "module": "ESNext",
5    "lib": ["ES2020", "DOM", "DOM.Iterable"],
6    "outDir": "./dist",
7    "rootDir": "./src",
8    "strict": true,
9    "esModuleInterop": true,
10    "skipLibCheck": true,
11    "forceConsistentCasingInFileNames": true
12  },
13  "include": ["src/**/*"],
14  "exclude": ["node_modules", "dist"]
15}

Les options clés du compilateur

Target - la version de JavaScript produite en sortie :

  • ES5 - compatibilité avec les anciens navigateurs
  • ES2020 - environnements modernes (nullish coalescing, optional chaining)
  • ESNext - les toutes dernières fonctionnalités

Module - le système de modules :

  • CommonJS - Node.js (require/module.exports)
  • ESNext - modules ES (import/export)
  • NodeNext - Node.js avec prise en charge des ESM

Lib - quelles API sont disponibles :

  • DOM - API du navigateur (document, window)
  • ES2020 - méthodes comme Promise.allSettled, BigInt
  • WebWorker - API des Web Workers

Strict Mode - sécurité maximale

Le flag strict: true active d'un coup toutes les vérifications rigoureuses. C'est comme activer la sécurité maximale à Jurassic Park : aucun dinosaure ne s'échappera de son enclos :

1// strict: true enables ALL of the following:
2{
3  "strictNullChecks": true,       // null/undefined must be handled
4  "strictFunctionTypes": true,    // strict typing of function parameters
5  "strictBindCallApply": true,    // strict typing of bind/call/apply
6  "strictPropertyInitialization": true, // properties must be initialized
7  "noImplicitAny": true,          // implicit any is forbidden
8  "noImplicitThis": true,         // this must have a type
9  "alwaysStrict": true,           // "use strict" in every file
10  "useUnknownInCatchVariables": true // catch(e) is unknown, not any
11}

Exemple de différence avec strictNullChecks :

1// WITHOUT strictNullChecks - runtime error!
2function getDinoName(id: string): string {
3  const dino = dinosaurs.find(d => d.id === id);
4  return dino.name; // Runtime error: Cannot read property 'name' of undefined
5}
6
7// WITH strictNullChecks - TypeScript enforces null handling
8function getDinoName(id: string): string | undefined {
9  const dino = dinosaurs.find(d => d.id === id);
10  return dino?.name; // Safe - returns undefined if not found
11}

Path Aliases - des raccourcis de navigation

Dans les gros projets, les imports peuvent devenir illisibles. Les path aliases permettent de créer des raccourcis :

1{
2  "compilerOptions": {
3    "baseUrl": ".",
4    "paths": {
5      "@models/*": ["src/models/*"],
6      "@utils/*": ["src/utils/*"],
7      "@services/*": ["src/services/*"],
8      "@config": ["src/config/index.ts"]
9    }
10  }
11}

Au lieu de :

1import { Dinosaur } from '../../../models/dinosaur';
2import { formatDate } from '../../utils/helpers';

Tu écris :

1import { Dinosaur } from '@models/dinosaur';
2import { formatDate } from '@utils/helpers';

Les fichiers de déclaration (.d.ts)

Les fichiers de déclaration décrivent les types pour du code JavaScript. Ils ont l'extension .d.ts et ne contiennent aucune implémentation, uniquement des signatures de types :

1// dinosaur.d.ts - type declaration
2declare interface IDinosaur {
3  id: string;
4  name: string;
5  species: string;
6  dangerLevel: 1 | 2 | 3 | 4 | 5;
7}
8
9declare function findDinosaur(id: string): IDinosaur | null;
10declare const PARK_NAME: string;
11
12// Module declaration for a JS library without types
13declare module 'dino-tracker' {
14  export function trackPosition(id: string): [number, number];
15  export function getStatus(id: string): 'active' | 'sleeping' | 'escaped';
16}

Trois types de fichiers de déclaration :

  1. Automatiques - TypeScript les génère avec le flag declaration: true
  2. Manuels - tu les écris toi-même pour du code JS sans types
  3. DefinitelyTyped - des types créés par la communauté pour les bibliothèques populaires

DefinitelyTyped (@types/*)

De nombreuses bibliothèques JavaScript n'ont pas de types intégrés. Le dépôt DefinitelyTyped contient des types créés par la communauté :

1# Installing types for popular libraries
2npm install --save-dev @types/node
3npm install --save-dev @types/express
4npm install --save-dev @types/lodash
5npm install --save-dev @types/jest

TypeScript reconnaît automatiquement les packages @types/* : tu n'as pas besoin de les importer. Si une bibliothèque a des types intégrés (comme axios ou date-fns), tu n'as pas besoin d'un package @types séparé.

L'option typeRoots dans tsconfig contrôle d'où TypeScript charge les types :

1{
2  "compilerOptions": {
3    "typeRoots": ["./node_modules/@types", "./src/types"]
4  }
5}

ESLint avec TypeScript

TypeScript a ses propres vérifications de types, mais ESLint avec le plugin @typescript-eslint ajoute des règles stylistiques et des analyses avancées :

1{
2  "parser": "@typescript-eslint/parser",
3  "plugins": ["@typescript-eslint"],
4  "extends": [
5    "eslint:recommended",
6    "plugin:@typescript-eslint/recommended",
7    "plugin:@typescript-eslint/recommended-requiring-type-checking"
8  ],
9  "parserOptions": {
10    "project": "./tsconfig.json"
11  },
12  "rules": {
13    "@typescript-eslint/no-explicit-any": "warn",
14    "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
15    "@typescript-eslint/prefer-nullish-coalescing": "error",
16    "@typescript-eslint/prefer-optional-chain": "error"
17  }
18}

Les règles @typescript-eslint les plus importantes :

  • no-explicit-any - avertit en cas d'utilisation de any
  • no-unused-vars - détecte les variables inutilisées (avec la prise en charge de TypeScript)
  • prefer-nullish-coalescing - préfère ?? à ||
  • prefer-optional-chain - préfère a?.b à a && a.b
  • strict-boolean-expressions - interdit les conversions booléennes implicites

Les options de qualité du code

Des flags de compilateur supplémentaires pour t'aider à maintenir la qualité du code :

1{
2  "compilerOptions": {
3    "noUnusedLocals": true,        // error on unused variables
4    "noUnusedParameters": true,    // error on unused parameters
5    "noImplicitReturns": true,     // every path must return a value
6    "noFallthroughCasesInSwitch": true, // enforces break in switch
7    "noUncheckedIndexedAccess": true,   // arr[i] is T | undefined
8    "exactOptionalPropertyTypes": true  // undefined !== omitting a field
9  }
10}

L'option noUncheckedIndexedAccess est particulièrement utile : elle protège contre les erreurs d'indexation :

1const dinos = ["T-Rex", "Raptor", "Triceratops"];
2
3// WITHOUT noUncheckedIndexedAccess
4const first: string = dinos[0]; // OK, but dinos[100] would also be "string"!
5
6// WITH noUncheckedIndexedAccess
7const first: string | undefined = dinos[0]; // TypeScript enforces checking
8if (first) {
9  console.log(first.toUpperCase()); // Safe
10}
Code de cette leçon : index.ts
1// Configuration TypeScript - Laboratoire du Parc Jurassique
2
3// === 1. STRICT MODE EN ACTION ===
4
5// strictNullChecks - impose la gestion de null/undefined
6interface IDinosaur {
7  id: string;
8  name: string;
9  species: string;
10  dangerLevel: 1 | 2 | 3 | 4 | 5;
11}
12
13const dinosaurs: IDinosaur[] = [
14  { id: "001", name: "Rexy", species: "T-Rex", dangerLevel: 5 },
15  { id: "002", name: "Blue", species: "Velociraptor", dangerLevel: 4 },
16  { id: "003", name: "Bumpy", species: "Ankylosaurus", dangerLevel: 2 },
17];
18
19// Recherche sécurisée avec gestion de undefined
20function findDino(id: string): IDinosaur | undefined {
21  return dinosaurs.find(d => d.id === id);
22}
23
24const dino = findDino("001");
25if (dino) {
26  console.log("Trouvé:", dino.name, "- niveau de danger:", dino.dangerLevel);
27} else {
28  console.log("Dinosaure introuvable");
29}
30
31// === 2. PATH ALIASES (simulation) ===
32// Dans un vrai projet:
33// import { Dinosaur } from '@models/dinosaur';
34// import { formatDate } from '@utils/helpers';
35// Au lieu de: import { Dinosaur } from '../../../models/dinosaur';
36
37// === 3. DECLARATION FILES (.d.ts) ===
38// Simulation de declaration de types pour une bibliothèque JS
39
40// Voici à quoi ressemblerait le fichier dino-tracker.d.ts:
41// declare module 'dino-tracker' {
42//   export function trackPosition(id: string): [number, number];
43//   export function getStatus(id: string): DinoStatus;
44// }
45
46// Nous utilisons les types comme si la bibliothèque existait:
47type DinoStatus = "active" | "sleeping" | "escaped";
48
49function getStatus(id: string): DinoStatus {
50  const dino = findDino(id);
51  if (!dino) return "escaped";
52  return dino.dangerLevel >= 4 ? "active" : "sleeping";
53}
54
55console.log("Status Rexy:", getStatus("001"));
56console.log("Status Blue:", getStatus("002"));
57
58// === 4. noUncheckedIndexedAccess ===
59const species = ["T-Rex", "Raptor", "Triceratops"];
60
61// Avec noUncheckedIndexedAccess: species[0] est string | undefined
62const first = species[0];
63if (first !== undefined) {
64  console.log("Première espèce:", first.toUpperCase());
65}
66
67// === 5. DEFINITELYTYPED ===
68// Installation des types:
69// npm install --save-dev @types/node @types/express @types/lodash
70// TypeScript les reconnaît automatiquement
71
72// === 6. ESLINT AVEC TYPESCRIPT ===
73// Exemples de règles @typescript-eslint:
74
75// prefer-nullish-coalescing: use ?? au lieu de ||
76const parkName: string | null = null;
77const displayName = parkName ?? "Jurassic Park"; // meilleur que ||
78console.log("Park:", displayName);
79
80// prefer-optional-chain: use ?. au lieu de &&
81interface ParkConfig {
82  security?: {
83    fences?: {
84      voltage?: number;
85    };
86  };
87}
88
89const config: ParkConfig = { security: { fences: { voltage: 10000 } } };
90const voltage = config.security?.fences?.voltage; // meilleur que config && config.security && ...
91console.log("Tension fence:", voltage, "V");
92
93// noImplicitReturns: chaque chemin doit retourner une value
94function classifyThreat(level: number): string {
95  if (level >= 4) return "DANGER ÉLEVÉ";
96  if (level >= 2) return "DANGER MOYEN";
97  return "DANGER FAIBLE"; // requis par noImplicitReturns
98}
99
100console.log("Classification (5):", classifyThreat(5));
101console.log("Classification (1):", classifyThreat(1));

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. Laquelle des options suivantes n’est PAS activée par l’option "strict": true dans tsconfig.json ?

  2. 2. Quel est le rôle des fichiers .d.ts (fichiers de déclaration) en TypeScript ?

Voici 2 des 4 questions de cette leçon. Réponds aux autres dans le jeu.

Tâches pratiques dans le jeu

  • Éditeur de code

    Corrige les problèmes du mode strict : strictNullChecks, noUncheckedIndexedAccess, noImplicitReturns, et configure les alias de chemin.

  • Éditeur de code

    Écris une interface SecurityEvent, implémente checkPerimeter et logEvent, et crée un type DinoSize avec une fonction classifyBySize.

  • Tri vertical

    Ordonne les options de tsconfig de la plus importante (à activer en premier) à la plus avancée :

  • Clique dans l'ordre

    Ordonne les éléments de configuration d’un alias de chemin dans tsconfig.json :

  • Tri vertical

    Ordonne les étapes de création d’un nouveau projet TypeScript :

Articles utiles