Leçon 8 / 8
Leçon 08 · TypeScript

TypeScript avec Node.js

Initialiser un projet Node.js + TypeScript

Un projet TypeScript pour Node.js se monte en quelques étapes. On initialise d'abord le projet Node.js, puis TypeScript, et on organise les dossiers.

Terminal
# 1. Initialiser le projet Node.js
npm init -y

# 2. Installer TypeScript
npm install --save-dev typescript

# 3. Générer le fichier tsconfig.json
npx tsc --init

Structure de dossiers recommandée :

Structure du projet
mon-projet/
├── src/           # sources TypeScript
│   └── index.ts
├── dist/          # JavaScript compilé (généré)
├── node_modules/
├── package.json
└── tsconfig.json

Installer @types/node

Node.js a ses propres API : fs, path, http, process… TypeScript ne connaît pas ces types par défaut. Le paquet @types/node les fournit.

Terminal
# Types Node.js pour TypeScript
npm install --save-dev @types/node

Une fois installé, tu bénéficies de l'autocomplétion et du typage strict pour toutes les API natives Node.js. Indispensable dès que tu importes fs ou path.

ts-node et ts-node-dev — exécuter TypeScript directement

Normalement, il faut compiler le TypeScript en JavaScript avant de l'exécuter. ts-node court-circuite cette étape : il exécute .ts directement dans Node.js. Pratique en développement.

Installation et utilisation
# Installer ts-node et ts-node-dev
npm install --save-dev ts-node ts-node-dev

# Exécuter un fichier TypeScript
npx ts-node src/index.ts

# Avec rechargement automatique (comme nodemon)
npx ts-node-dev src/index.ts

ts-node-dev surveille les fichiers et redémarre automatiquement à chaque modification, exactement comme nodemon mais avec TypeScript natif. Utilise-le systématiquement pendant le développement — tu évites de taper tsc && node dist/… à chaque changement.

tsconfig.json pour Node.js

Le fichier tsconfig.json généré par tsc --init contient beaucoup d'options commentées. Voici une configuration minimaliste et efficace pour Node.js :

tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",         // Version JS cible
    "module": "commonjs",      // Format de modules Node.js
    "rootDir": "./src",         // Sources TypeScript
    "outDir": "./dist",         // Destination du JS compilé
    "strict": true,             // Toutes les vérifications strictes
    "esModuleInterop": true,    // Compatibilité imports CommonJS
    "skipLibCheck": true,       // Ignore les erreurs dans node_modules
    "resolveJsonModule": true  // Permet d'importer des .json
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"]
}

Les deux clés les plus importantes : "rootDir" indique où se trouvent tes sources, "outDir" où TypeScript écrit le JavaScript compilé. TypeScript reproduit la structure de src/ dans dist/.

Scripts npm : build, dev, start

Centralise toutes les commandes dans package.json. Un projet TypeScript Node.js a typiquement trois scripts essentiels :

package.json — scripts
{
  "scripts": {
    "build": "tsc",
    "dev":   "ts-node-dev --respawn src/index.ts",
    "start": "node dist/index.js"
  }
}
  • npm run build — compile tout le TypeScript vers dist/
  • npm run dev — démarre avec rechargement automatique, sans compilation préalable
  • npm start — exécute le JavaScript compilé en production

Typer les modules Node.js : fs, path, http

Avec @types/node installé, tous les modules natifs Node.js sont typés. Les fonctions retournent les bons types, les erreurs sont catchées avec le bon type.

src/exemples-node.ts
import fs from "fs";
import path from "path";
import http from "http";

// fs : lecture d'un fichier texte
const contenu: string = fs.readFileSync("data.txt", "utf-8");

// path : construction de chemins portables
const chemin: string = path.join(__dirname, "data", "config.json");

// http : serveur HTTP minimal
const server = http.createServer(
  (req: http.IncomingMessage, res: http.ServerResponse) => {
    res.writeHead(200, { "Content-Type": "text/plain" });
    res.end("Bonjour depuis TypeScript !");
  }
);

server.listen(3000, () => {
  console.log("Serveur sur http://localhost:3000");
});

Gestion des erreurs typée

En TypeScript, le type natif Error est disponible partout. Pour aller plus loin, tu peux créer des classes d'erreur personnalisées qui étendent Error — utile pour distinguer les erreurs métier des erreurs système.

src/errors.ts
// Erreur personnalisée pour les fichiers introuvables
class FichierIntrouvableError extends Error {
  constructor(public chemin: string) {
    super(`Fichier introuvable : ${chemin}`);
    this.name = "FichierIntrouvableError";
  }
}

// Utilisation avec instanceof pour filtrer le type d'erreur
function lireFichier(chemin: string): string {
  try {
    return fs.readFileSync(chemin, "utf-8");
  } catch (e) {
    throw new FichierIntrouvableError(chemin);
  }
}

try {
  const contenu = lireFichier("manquant.txt");
} catch (e) {
  if (e instanceof FichierIntrouvableError) {
    console.error("Chemin :", e.chemin);
  }
}
⚠️

Avec strict: true, le paramètre e d'un bloc catch est de type unknown — pas any. Tu dois vérifier son type avec instanceof avant d'accéder à ses propriétés. C'est volontaire : TypeScript t'oblige à ne pas supposer ce qui a été lancé.

Checklist de bonnes pratiques

Un projet TypeScript Node.js bien configuré respecte ces points dès le départ. Mieux vaut les mettre en place au début que corriger 200 erreurs plus tard.

  • strict: true dans tsconfig.json. Active toutes les vérifications : noImplicitAny, strictNullChecks, noImplicitThis… Ne pas le désactiver parce que "ça fait des erreurs" — les erreurs sont le point.
  • Pas de any implicite. Si tu dois contourner le typage, utilise unknown puis affine avec des gardes de type. Le any explicite avec un commentaire est acceptable ponctuellement — le any silencieux, jamais.
  • Linter : ESLint + typescript-eslint. TypeScript détecte les erreurs de types. ESLint détecte les mauvaises pratiques. Les deux sont complémentaires.
  • Ajoute dist/ et node_modules/ dans .gitignore. Le dossier dist/ est généré — il ne va pas dans le dépôt Git.
  • En production, compile d'abord, exécute ensuite. ts-node est réservé au développement. En prod : npm run build && npm start.
Installation ESLint + typescript-eslint
npm install --save-dev eslint @eslint/js typescript-eslint

# eslint.config.js (format plat, ESLint v9+)
eslint.config.js
import eslint from "@eslint/js";
import tseslint from "typescript-eslint";

export default tseslint.config(
  eslint.configs.recommended,
  ...tseslint.configs.recommended
);
// À retenir — Bilan du module TypeScript
  • Pour Node.js : npm install --save-dev typescript @types/node, puis npx tsc --init.
  • ts-node-dev exécute et recharge le TypeScript directement en développement.
  • Dans tsconfig.json : configure rootDir, outDir, target et module: "commonjs".
  • Les types des modules Node.js viennent de @types/node — sans eux, pas d'autocomplétion.
  • Active strict: true dès le départ. Ajoute ESLint + typescript-eslint pour aller plus loin.
  • Bilan du module : tu sais typer des variables, fonctions, objets, interfaces et génériques. Tu maîtrises les types avancés, la POO orientée TypeScript, les modules, et maintenant l'intégration Node.js. TypeScript n'est plus une contrainte — c'est un filet de sécurité qui te fait écrire un meilleur code.