Le mode strict de TypeScript active une série de checks (strictNullChecks, noImplicitAny, noUncheckedIndexedAccess, etc.) qui transforment radicalement la fiabilité d'une base de code. Encore faut-il pouvoir migrer un projet existant sans bloquer la livraison de features. Chez Krealabs, tous nos projets sont en strict mode depuis 2020, et on a accompagné une douzaine de clients dans la migration de leur base de code non-stricte. Voici ce qu'on a appris : ce que strict change vraiment, comment migrer progressivement, et les bénéfices mesurables qu'on observe après 1-2 ans.
01Ce que strict change vraiment
Le mode strict force à gérer explicitement les cas null/undefined (strictNullChecks), les types implicites any (noImplicitAny), les fonctions qui ne couvrent pas tous les cas (strictFunctionTypes), et les méthodes appelées sur des valeurs potentiellement nulles (alwaysStrict). Sur une base de code typique de 50k lignes, l'activation révèle 100 à 500 bugs latents — la plupart silencieux en production : variables undefined dans certains edge cases, propriétés d'objet manquantes, fonctions qui retournent parfois undefined sans que personne ne le sache. Les développeurs qui ont vécu une migration n'imaginent plus revenir en arrière.
02L'option qui change tout : noUncheckedIndexedAccess
C'est l'option la moins connue et la plus impactante. Sans elle, `array[0]` est typé comme le type du tableau (T), pas T | undefined. Or, l'index 0 d'un tableau vide est undefined ! Avec noUncheckedIndexedAccess, TypeScript force à vérifier l'existence avant utilisation. C'est verbeux au début mais ça élimine une catégorie entière de bugs `TypeError: Cannot read property X of undefined`.
// Sans noUncheckedIndexedAccess
const first = users[0]
console.log(first.name) // Crash si users est vide
// Avec noUncheckedIndexedAccess
const first = users[0]
console.log(first.name) // ❌ TypeScript : first peut être undefined
console.log(first?.name) // ✅ OK
if (first) console.log(first.name) // ✅ OK03Activer progressivement, par dossier
Pas besoin de tout activer d'un coup. Commencez par noImplicitAny (souvent gérable, force à typer les paramètres), puis strictNullChecks (le plus impactant, force à gérer les null/undefined), puis le reste. Vous pouvez aussi configurer strict par dossier en utilisant plusieurs tsconfig.json avec extends. Sur les projets Next.js, on commence souvent par strict sur les API routes et utilities, puis on étend aux composants UI.
// tsconfig.json
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
// ou progressif :
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true
}
}
// tsconfig.strict.json (pour un sous-dossier audité)
{
"extends": "./tsconfig.json",
"compilerOptions": { "strict": true, "noUncheckedIndexedAccess": true },
"include": ["./src/api/**/*", "./src/lib/**/*"]
}04Outils pour la migration automatisée
ts-migrate de Airbnb automatise une partie (ajoute des annotations `// @ts-expect-error` aux endroits qui posent problème, type les paramètres any en `any` explicite, etc.). Pour les erreurs restantes, utilisez @ts-expect-error avec un commentaire TODO daté, plutôt que @ts-ignore. La différence : @ts-expect-error échoue si l'erreur n'existe plus (force le nettoyage), @ts-ignore est silencieux pour toujours. ESLint avec @typescript-eslint/ban-ts-comment peut interdire @ts-ignore. Combiner avec un fichier `TYPESCRIPT_DEBT.md` qui liste les zones à reprendre.
05Bénéfices mesurés après 1-2 ans
Sur les projets clients où on a piloté la migration en 2023-2024, on a mesuré : 1) Réduction de 30 à 50% des bugs de production liés à `undefined` ou `null` (rapport Sentry). 2) Refactorings 3x plus rapides (changer un type propage les erreurs partout, on sait exactement quoi mettre à jour). 3) Onboarding des nouveaux développeurs accéléré (les types servent de documentation vivante). 4) Moins de tests unitaires à écrire pour cas null/undefined (le compilateur les attrape). Coût initial : 2-4 semaines selon la taille du codebase. Retour sur investissement : moins d'un an.
06Combiner avec Zod et runtime validation
TypeScript est purement statique : il disparaît à la compilation. Pour valider les données qui ENTRENT dans votre app (API responses, formulaires utilisateur, query params), utilisez une bibliothèque de runtime validation comme Zod, Valibot ou TypeBox. Zod génère le type TypeScript automatiquement à partir du schéma, donc une seule source de vérité. Pattern qu'on utilise chez Krealabs partout : valider les payloads d'API avec Zod, le type est inféré, plus aucun cast manuel.
import { z } from 'zod'
const UserSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
age: z.number().int().positive().optional(),
})
type User = z.infer<typeof UserSchema> // Type généré
// Validation runtime
const user = UserSchema.parse(req.body) // Throw si invalide
// user est maintenant typé ET validé07Les libs tierces non-strict : que faire
Le frein principal à strict mode dans un projet existant : les bibliothèques tierces dont les types sont mal écrits ou pas à jour. Solutions : 1) Préférer les libs maintenues avec des types corrects (TanStack Query, Zod, Prisma — excellents). 2) Pour les libs avec mauvais types, écrire un wrapper typé qui isole l'incohérence. 3) En dernier recours, declare module 'lib-name' pour overrider les types. 4) Contribuer aux types upstream si possible (DefinitelyTyped) — geste citoyen et ça résout pour tout le monde.
Tous nos projets Krealabs démarrent en strict mode + noUncheckedIndexedAccess. C'est non négociable : le coût initial est minime, le bénéfice sur 2-3 ans est énorme. Si vous héritez d'une base de code non stricte, la migration vaut largement l'investissement — mais demande du temps dédié, pas seulement quelques heures volées entre deux features. Si vous voulez accompagnement pour migrer votre base de code TypeScript vers strict, c'est exactement le type de mission qu'on adore.
Écrit par
Maxime Dubois
Co-fondateur · Krealabs
Découvrir l'équipe



