ITSkillsCenter
Blog

TypeScript tsconfig strict en pratique : guide complet

13 min de lecture

Lecture : 12 minutes · Niveau : intermédiaire · Mise à jour : avril 2026

tsconfig.json est le fichier qui dicte le comportement du compilateur TypeScript. Mal configuré, il laisse passer des erreurs et limite la valeur du langage. Bien configuré, il transforme TypeScript en filet de sécurité solide. Ce guide passe en revue chaque option qui compte vraiment et donne des recommandations applicables tout de suite.

Voir aussi → TypeScript et JavaScript moderne : guide pratique.


Sommaire

  1. Pourquoi strict: true n’est qu’un début
  2. Les options ciblées par strict
  3. Options strictes additionnelles
  4. Modules : NodeNext, Bundler, ou autre
  5. Target et lib
  6. Paths aliases pour imports propres
  7. Source maps et déclarations
  8. tsconfig pour différents contextes
  9. Performance de compilation
  10. FAQ

1. Pourquoi strict: true n’est qu’un début

strict: true active une famille d’options qui ensemble forcent une bonne discipline de typage. C’est le minimum syndical pour un projet sérieux. Mais TypeScript propose des options encore plus strictes en dehors de cette famille — souvent désactivées par défaut alors qu’elles évitent des classes entières de bugs réels.

L’objectif d’une config stricte n’est pas de torturer les développeurs : c’est d’attraper à la compilation ce qui sinon planterait en production avec un message obscur à 3h du matin. Chaque option supplémentaire activée représente une catégorie de bugs que le compilateur garantit absente du code.

// Le strict minimum à connaître
{
  "compilerOptions": {
    "strict": true,                      // active la famille strict
    "noUncheckedIndexedAccess": true,    // accès tableau/objet → undefined possible
    "noImplicitOverride": true,          // override explicite obligatoire
    "noFallthroughCasesInSwitch": true,  // pas de chute de case sans break
    "noUnusedLocals": true,              // variables locales inutilisées
    "noUnusedParameters": true,          // paramètres inutilisés
    "exactOptionalPropertyTypes": true,  // strict sur les optionnels
    "noPropertyAccessFromIndexSignature": true
  }
}

2. Les options ciblées par strict

strict: true est équivalent à activer toutes ces options ensemble :

strictNullChecks

function trouver(id: number): User | undefined {
  return users.find(u => u.id === id);
}

// strictNullChecks: false (mauvais)
const nom = trouver(1).nom;  // bug à l'exécution si pas trouvé

// strictNullChecks: true (bon)
const u = trouver(1);
if (u) {
  console.log(u.nom);
}

Cette option seule attrape probablement la majorité des bugs null is not an object qu’on rencontre en JavaScript.

noImplicitAny

// noImplicitAny: false (mauvais)
function process(data) {  // data est any implicite
  return data.foo;
}

// noImplicitAny: true (bon)
function process(data: unknown) {
  // forcer le typage explicite
}

Force à typer explicitement les paramètres et retours quand l’inférence ne peut pas le faire.

strictFunctionTypes

Vérifie la covariance des paramètres de fonction avec plus de rigueur. Concept avancé mais utile pour éviter des cas pernicieux.

strictBindCallApply

Vérifie les types lors de l’usage de .bind(), .call(), .apply() sur les fonctions.

strictPropertyInitialization

class Service {
  private client: Client;  // erreur : pas initialisé

  constructor() {
    // oubli d'initialiser this.client
  }
}

Force à initialiser les propriétés de classe dans le constructeur ou avec une valeur par défaut.

alwaysStrict

Émet "use strict" en tête des fichiers JavaScript produits. Standard moderne.

noImplicitThis

Erreur si this est implicitement de type any.

useUnknownInCatchVariables

try {
  // ...
} catch (e) {  // e est `unknown` (pas any)
  if (e instanceof Error) {
    console.log(e.message);
  }
}

unknown force à vérifier le type avant utilisation, contrairement à any qui désactive le contrôle.


3. Options strictes additionnelles

Ces options ne sont pas dans strict mais sont fortement recommandées.

noUncheckedIndexedAccess

C’est probablement la plus impactante après strict.

const liste: string[] = ["a", "b", "c"];

// Sans noUncheckedIndexedAccess
const x = liste[10];        // x: string (FAUX)
console.log(x.toUpperCase());  // crash à l'exécution

// Avec noUncheckedIndexedAccess
const x = liste[10];        // x: string | undefined
if (x) {
  console.log(x.toUpperCase());
}

TypeScript par défaut ne signale pas qu’un accès à un index hors-limites peut retourner undefined. Cette option corrige ce trou. Coût : il faut vérifier ou non-null-asserter (!) à chaque accès indexé. Bénéfice : zéro Cannot read property of undefined en production.

exactOptionalPropertyTypes

interface Config {
  port?: number;
}

const c: Config = { port: undefined };  // erreur avec exactOptionalPropertyTypes

Distingue clairement « propriété absente » et « propriété présente avec valeur undefined ». Évite des bugs subtils.

noUnusedLocals et noUnusedParameters

Erreur sur les variables ou paramètres déclarés mais jamais lus. Force à nettoyer le code mort. Pour les paramètres volontairement ignorés, préfixer par _ les exclut de la vérification.

noImplicitReturns

function classer(x: number): string {
  if (x > 0) return "positif";
  // erreur : pas de return dans toutes les branches
}

noFallthroughCasesInSwitch

switch (val) {
  case 1:
    console.log("un");
    // erreur : pas de break
  case 2:
    console.log("deux");
    break;
}

noPropertyAccessFromIndexSignature

Pour les objets avec une signature d’index, force obj["key"] au lieu de obj.key. Distinction utile pour la lisibilité.

noImplicitOverride

class A {
  greet() { /* ... */ }
}
class B extends A {
  override greet() { /* ... */ }  // mot-clé `override` obligatoire
}

Empêche d’override accidentellement une méthode parent.


4. Modules : NodeNext, Bundler, ou autre

Le choix de module et moduleResolution est plus subtil qu’il n’y paraît et a beaucoup évolué.

Pour Node.js récent (LTS 20+)

{
  "module": "NodeNext",
  "moduleResolution": "NodeNext"
}

NodeNext fait correspondre exactement le comportement réel de Node.js, y compris les distinctions ESM/CJS modernes. Demande des extensions .js explicites dans les imports même pour les fichiers .ts :

// Imports avec extension
import { foo } from "./util.js";  // pointe vers util.ts en source

Pour bundler (Vite, esbuild, webpack)

{
  "module": "ESNext",
  "moduleResolution": "Bundler"
}

Bundler est plus permissif : pas besoin d’extensions explicites, le bundler gère la résolution.

À éviter en 2026

module: "CommonJS" reste valide mais limité aux projets legacy Node. moduleResolution: "Node" est l’ancien comportement, dépassé.


5. Target et lib

target

Détermine la version JavaScript émise.

{ "target": "ES2022" }   // recommandé en 2026 pour serveur
{ "target": "ES2020" }   // pour navigateurs un peu plus anciens

Plus le target est récent, plus le code émis est compact et lisible (les fonctionnalités modernes ne sont pas transpilées). Mais doit être supporté par la cible d’exécution.

lib

Quels types de bibliothèques sont disponibles globalement.

{ "lib": ["ES2022", "DOM"] }   // navigateur + JS moderne
{ "lib": ["ES2022"] }          // Node.js, pas de DOM
{ "lib": ["ES2022", "WebWorker"] }  // worker

Sans DOM, TypeScript signalera document is not defined dans un contexte serveur — utile pour s’assurer qu’on n’utilise pas accidentellement des APIs navigateur dans du code Node.


6. Paths aliases pour imports propres

Pour éviter les import "../../../../utils/foo" à n’en plus finir :

{
  "compilerOptions": {
    "baseUrl": "./src",
    "paths": {
      "@/*": ["*"],
      "@components/*": ["components/*"],
      "@lib/*": ["lib/*"]
    }
  }
}

Usage :

import { Button } from "@components/Button";
import { fetcher } from "@lib/fetcher";

Attention : ces alias doivent aussi être configurés au niveau du runtime ou bundler (Vite, Node avec tsx, Jest avec moduleNameMapper). TypeScript seul ne suffit pas pour l’exécution.


7. Source maps et déclarations

Source maps

{
  "sourceMap": true,
  "inlineSources": true
}

Permet de déboguer le code TypeScript original même quand on exécute le JavaScript compilé. Indispensable en développement, optionnel en production selon politique de débogage.

Fichiers de déclaration

Pour une bibliothèque :

{
  "declaration": true,
  "declarationMap": true,
  "outDir": "./dist"
}

Génère les fichiers .d.ts à côté du JavaScript. declarationMap permet au navigateur « go to definition » de pointer vers le source TypeScript original.


8. tsconfig pour différents contextes

Application Node.js / API

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "lib": ["ES2022"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "resolveJsonModule": true,
    "sourceMap": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

Application Frontend (Vite, React)

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "lib": ["ES2022", "DOM", "DOM.Iterable"],
    "jsx": "react-jsx",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noEmit": true,           // Vite gère le build
    "isolatedModules": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "allowImportingTsExtensions": true
  },
  "include": ["src/**/*"]
}

noEmit: true car le bundler (Vite) compile, TypeScript fait juste la vérification de types. isolatedModules: true requis pour les bundlers qui compilent fichier par fichier.

Bibliothèque publiée

Combinaison qui produit JS + .d.ts pour distribution :

{
  "compilerOptions": {
    "target": "ES2020",       // un peu plus conservateur pour large compatibilité
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  }
}

9. Performance de compilation

Sur des gros projets (50k+ lignes), le compilateur peut devenir lent. Quelques leviers :

incremental

{ "incremental": true, "tsBuildInfoFile": "./.tsbuildinfo" }

Cache la dernière compilation, recompile seulement ce qui a changé. Gain spectaculaire sur les builds répétés.

skipLibCheck

Saute la vérification des fichiers .d.ts des dépendances. Active par défaut dans la plupart des templates modernes. Gain significatif, risque limité.

Project references

Pour des monorepos : découper la base en projets TypeScript séparés avec references. Chaque projet est compilé indépendamment et peut être mis en cache.

{
  "references": [
    { "path": "../shared" },
    { "path": "../api" }
  ]
}

Outils alternatifs pour le build

esbuild, swc ou tsc avec --watch peuvent être plus rapides selon le besoin. tsc reste le seul à faire la vérification de types complète. Pour la prod : tsc --noEmit pour la vérif + esbuild pour la compilation effective est un combo populaire.

Voir aussi → JavaScript moderne : patterns pratiques pour les patterns de code qui exploitent au mieux ces vérifications.


10. FAQ

Faut-il toujours activer noUncheckedIndexedAccess ?

Oui pour un nouveau projet. Sur un projet existant, c’est l’option strictes qui fait le plus de bruit à l’activation (beaucoup d’erreurs apparaissent). À introduire après les autres options strictes, en corrigeant progressivement.

strict: true casse mon legacy, comment migrer progressivement ?

Activer strict: false mais activer une à une les options qui le composent : strictNullChecks, puis noImplicitAny, puis les autres. Permet une migration par étapes maîtrisées. Voir Migrer un projet JavaScript vers TypeScript.

skipLibCheck désactive-t-il vraiment des protections importantes ?

Pas vraiment en pratique. Les .d.ts des bibliothèques sont supposés corrects, et leurs erreurs propres ne devraient pas bloquer la compilation de votre code. Activer skipLibCheck: true est presque toujours la bonne décision.

Puis-je avoir plusieurs tsconfig dans un même projet ?

Oui. Pattern courant : tsconfig.json racine pour la config commune, tsconfig.build.json qui étend pour la production (exclut tests, source maps allégés), tsconfig.test.json pour les tests. Utiliser extends: "./tsconfig.json" pour hériter.

target: "ESNext" ou une version fixe ?

Préférer une version fixe (ES2022 par exemple) à ESNext. ESNext signifie « la version la plus récente connue par cette version de TypeScript », ce qui change implicitement à chaque mise à jour de TypeScript. Une version fixe rend le comportement prévisible et reproductible.

Comment gérer un fichier qui doit être compilé avec une autre config ?

Avec un tsconfig séparé qui inclut juste ce fichier, ou avec des // @ts-ignore / // @ts-expect-error localisés (à utiliser avec parcimonie). Pour des cas comme l’utilisation de import.meta qui requiert un module spécifique, un fichier dédié peut être judicieux.

Y a-t-il des options à éviter absolument ?

noEmitOnError: false est dangereux : permet de produire du JavaScript même quand TypeScript signale des erreurs. Mieux vaut le laisser à true (défaut) et corriger les erreurs. allowJs n’est utile qu’en migration progressive ; à désactiver une fois la migration terminée.


Articles liés (cluster TypeScript et JavaScript moderne)


Article mis à jour le 25 avril 2026. Pour signaler une erreur ou suggérer une amélioration, écrivez-nous.

Besoin d'un site web ?

Confiez-nous la Création de Votre Site Web

Site vitrine, e-commerce ou application web — nous transformons votre vision en réalité digitale. Accompagnement personnalisé de A à Z.

À partir de 250.000 FCFA
Parlons de Votre Projet
Publicité