ITSkillsCenter
Blog

Migrer un projet JavaScript vers TypeScript : guide pratique

13 min de lecture

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

Migrer un projet JavaScript existant vers TypeScript ne doit pas être un big bang. Mal préparée, la migration provoque des semaines de blocage et un découragement de l’équipe. Bien planifiée, elle se fait en arrière-plan sans interrompre les livraisons et apporte une valeur croissante au fil des semaines. Ce guide trace une stratégie pragmatique éprouvée sur des projets de production.

Voir aussi → TypeScript et JavaScript moderne : guide pratique.


Sommaire

  1. Évaluer si la migration en vaut la peine
  2. Stratégie progressive vs big bang
  3. Setup initial sans bloquer
  4. Première vague : ajouter les types sans renommer
  5. Deuxième vague : renommer .js en .ts
  6. Troisième vague : durcir progressivement
  7. Gérer les bibliothèques externes
  8. Outils et automatisation
  9. Communiquer avec l’équipe
  10. FAQ

1. Évaluer si la migration en vaut la peine

Avant de migrer, vérifier que ça vaut le coût.

Bons candidats à la migration : projet actif avec développement régulier, équipe de 2+ développeurs, base de code 5k+ lignes, bugs récurrents liés aux types (passage de mauvais objet, propriété inexistante, undefined non géré), refactoring fréquents, intégration avec des bibliothèques bien typées.

Migration discutable : projet en mode maintenance pure (très peu de changements), équipe très petite et stable, taille modeste, deadline serrée à court terme qui rendrait la migration dangereuse.

Migration probablement inutile : script ad-hoc qui tourne une fois, projet en fin de vie qui sera remplacé bientôt.

Un signal pratique : si l’équipe consulte régulièrement la console au runtime pour découvrir la forme exacte d’un objet manipulé, TypeScript apporterait une valeur immédiate. Si l’équipe a déjà une excellente discipline JSDoc et de tests, le gain sera plus marginal.


2. Stratégie progressive vs big bang

Deux écoles existent, mais la pratique a tranché : la migration progressive gagne.

Big bang : tout convertir en une seule branche, puis merger. Avantages : pas de mode mixte, code 100% TypeScript d’un coup. Inconvénients : énorme PR difficile à reviewer, conflit avec toutes les PRs en cours pendant la migration, risque élevé de bugs subtils introduits, démotivation si la migration prend des semaines bloquantes.

Progressive : configurer TypeScript pour accepter à la fois des fichiers .ts et .js, convertir fichier par fichier en parallèle des autres tâches. Avantages : pas de blocage, livraisons continues, valeur ajoutée incrémentale (chaque fichier converti est un gain). Inconvénients : période de cohabitation plus longue, discipline nécessaire pour ne pas l’oublier en route.

Pour 95% des projets, l’approche progressive est la bonne.


3. Setup initial sans bloquer

L’objectif du setup est d’ajouter TypeScript sans casser ce qui marche.

npm install -D typescript @types/node tsx
npx tsc --init

tsconfig.json permissif au démarrage :

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "outDir": "./dist",
    "rootDir": "./src",

    "allowJs": true,
    "checkJs": false,

    "strict": false,
    "noImplicitAny": false,
    "strictNullChecks": false,

    "esModuleInterop": true,
    "skipLibCheck": true,
    "resolveJsonModule": true,
    "sourceMap": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

Points clés :
allowJs: true permet à TypeScript de gérer les .js aux côtés des .ts
checkJs: false ne vérifie pas les .js (sinon trop d’erreurs immédiates)
strict: false au début, on durcira plus tard

Ajouter aussi un script de check dans package.json :

{
  "scripts": {
    "type-check": "tsc --noEmit"
  }
}

Cela permet de vérifier les types sans produire de fichiers — utile en CI.

À ce stade, le projet compile et tourne exactement comme avant. La seule différence : on peut maintenant créer des fichiers .ts à côté des .js existants.


4. Première vague : ajouter les types sans renommer

Avant même de renommer, on peut typer du code JavaScript via des commentaires JSDoc. TypeScript les comprend.

/**
 * @param {string} email
 * @returns {boolean}
 */
function valider(email) {
  return /^[^@]+@[^@]+\.[^@]+$/.test(email);
}

/**
 * @typedef {Object} Client
 * @property {number} id
 * @property {string} nom
 * @property {string} email
 * @property {string} [telephone]
 */

/** @type {Client} */
const c = { id: 1, nom: "Acme", email: "contact@acme.test" };

Activer checkJs: true (au moins pour quelques fichiers via // @ts-check en tête) commence à faire vérifier ces annotations. Bénéfice : on commence à attraper des bugs sans avoir renommé un seul fichier.

Cette étape est optionnelle mais utile pour des équipes qui veulent voir le bénéfice de TypeScript avant de s’engager dans le renommage massif.


5. Deuxième vague : renommer .js en .ts

Maintenant on commence le vrai travail. Stratégie : renommer un fichier à la fois, par PR séparée si possible.

Par où commencer

L’ordre optimal :

  1. Fichiers feuilles : modules sans dépendance interne (utils, helpers, constants)
  2. Modules avec peu de dépendances : modèles de données, validateurs
  3. Logique métier : services, contrôleurs
  4. Points d’entrée : main.ts, index.ts

Cet ordre limite les erreurs en cascade. Chaque fichier renommé bénéficie déjà des types des dépendances précédemment converties.

Que faire à la conversion

Pour chaque fichier renommé .js.ts :

  1. Renommer
  2. Lancer npm run type-check
  3. Corriger les erreurs apparues, par ordre de gravité
  4. Si une erreur est trop coûteuse à corriger immédiatement : // @ts-expect-error avec un commentaire explicatif et un TODO. À retirer plus tard.
  5. Commiter

Quelques transformations courantes :

// JS
function additionner(a, b) {
  return a + b;
}

// TS
function additionner(a: number, b: number): number {
  return a + b;
}
// JS
const config = {};
config.port = process.env.PORT || 3000;

// TS
const config: { port: number | string } = {};
config.port = process.env.PORT || 3000;
// ou mieux : interface
interface Config { port: number | string; }
const config: Config = { port: process.env.PORT || 3000 };

Quand any est acceptable temporairement

Pendant la migration, any peut servir de marqueur. Plutôt que de bloquer 2 heures à typer correctement une fonction complexe :

// TODO: type properly
function process(data: any): any {
  // ...
}

Mieux vaut un fichier converti avec quelques any qu’un fichier qui reste en .js. Garder une liste des any à éliminer plus tard, traités lors de sprints dédiés ou en accompagnement de modifications futures.


6. Troisième vague : durcir progressivement

Une fois la majorité (~80%) du code en TypeScript, durcir la configuration.

Activer ces options dans cet ordre, en corrigeant les erreurs entre chaque :

  1. noImplicitAny: true
  2. strictNullChecks: true (souvent l’étape qui révèle le plus de bugs)
  3. strictFunctionTypes: true
  4. strict: true (active toutes les options de la famille strict)
  5. noUnusedLocals et noUnusedParameters
  6. noUncheckedIndexedAccess: true (peut faire beaucoup de bruit)
  7. Désactiver allowJs une fois 100% migré

Chaque palier demande typiquement quelques heures à quelques jours de travail selon la taille de la base. Faire ces durcissements pendant des sprints calmes, pas pendant des urgences business.

Détail des options dans TypeScript tsconfig strict en pratique.


7. Gérer les bibliothèques externes

Pour chaque dépendance utilisée :

Cas 1 : la lib expose ses types nativement

La majorité des bibliothèques modernes (React, Express, Prisma, etc.) sont écrites en TypeScript ou exposent des typings officiels. Aucune action.

Cas 2 : un paquet @types/... existe sur DefinitelyTyped

npm install -D @types/lodash @types/express

Ces paquets communautaires couvrent la plupart des bibliothèques populaires.

Cas 3 : aucun typing existant

Rare en 2026, mais possible pour des libs de niche. Solutions :

// Option A : déclaration minimaliste dans un .d.ts
declare module "ma-lib-non-typee";  // implicit any partout

// Option B : déclaration partielle
declare module "ma-lib-non-typee" {
  export function init(config: { apiKey: string }): void;
  export function envoyer(message: string): Promise<boolean>;
}

Option A est rapide mais perd la sécurité. Option B demande quelques heures mais apporte de la valeur durable. Pour une dépendance critique : envisager de contribuer le typing au repo upstream pour bénéficier à la communauté.


8. Outils et automatisation

Conversion semi-automatique

ts-migrate (Airbnb), dts-gen, et autres outils peuvent automatiser une partie du travail. À utiliser avec prudence : ils ajoutent souvent beaucoup de any qu’il faut nettoyer ensuite. La conversion manuelle reste plus propre.

Lint pour pousser à la rigueur

ESLint avec le plugin typescript-eslint permet d’imposer des règles :

  • @typescript-eslint/no-explicit-any : warn ou error sur les any explicites
  • @typescript-eslint/no-unsafe-* : warn sur les opérations non-sûres
  • @typescript-eslint/strict-boolean-expressions : force les conditions explicites

Ces règles sont à activer progressivement pendant la migration, pas en bloc.

CI bloquant

Une fois la base raisonnablement typée, ajouter un step CI qui fait tsc --noEmit et fait échouer le build si erreurs. Cela protège contre les régressions.

# Dans GitHub Actions
- name: Type check
  run: npm run type-check

Voir GitHub Actions tutoriel.


9. Communiquer avec l’équipe

Une migration longue (souvent 2-4 mois pour un projet moyen, plus pour un gros) nécessite de l’alignement.

Points à clarifier

  • Pourquoi on migre : exposer le bénéfice attendu (moins de bugs, meilleure auto-complétion), pas juste « parce que c’est moderne »
  • Combien de temps : estimation honnête, sachant que ça déborde toujours un peu
  • Qui fait quoi : un référent qui pilote, mais idéalement toute l’équipe contribue à son rythme
  • Règle de PR pendant la migration : on n’écrit plus de nouveau code en .js ; toute nouvelle fonctionnalité va directement en .ts
  • Quand durcit-on : annoncer en avance les durcissements de tsconfig pour que personne soit surpris

Documenter le statut

Tenir un compteur quelque part (un README dédié, un wiki) :

Migration TypeScript : 142 / 287 fichiers (49%)
- src/utils/* : 100%
- src/services/* : 78%
- src/components/* : 35%
- src/pages/* : 12%

Cela donne de la visibilité et motive l’équipe.


10. FAQ

Combien de temps pour migrer un projet de 50k lignes ?

Variable selon discipline et taille d’équipe : typiquement 2 à 6 mois en parallèle des autres tâches, avec 1-2 personnes qui poussent activement. Plus si la base est dense en logique typée complexe (formulaires, états redux, etc.).

Faut-il refactorer pendant la migration ?

Non, séparer les deux. Migrer un fichier sans changer sa logique, dans une PR dédiée. Refactorer dans une autre PR si nécessaire. Mélanger les deux brouille les revues et augmente le risque de bugs.

Mon projet utilise du Redux/Vuex/Pinia mal typé, comment migrer ?

Commencer par typer les états et actions/mutations dans des fichiers dédiés. Les frameworks d’état modernes (Pinia, Zustand, Redux Toolkit avec TypeScript) sont conçus pour TypeScript. Pour du legacy Redux non-typé, c’est l’une des parties les plus coûteuses de la migration.

Faut-il convertir les fichiers de test aussi ?

Oui, en même temps que les fichiers qu’ils testent. Avoir des tests typés assure que les types sont effectivement testés et utilisés correctement.

Et si une partie du code reste en .js définitivement ?

Acceptable s’il s’agit de scripts d’admin ponctuels, code legacy qui sera supprimé bientôt, ou contraintes externes. Mais pour le code de production actif, viser 100% TypeScript apporte le plein bénéfice. Mode hybride durable est un compromis qui finit par coûter cher en mental load.

Comment justifier la migration au business ?

Mesurer les bugs liés aux types avant et après sur 3 mois. Un projet typique voit une réduction significative de cette catégorie de bugs après TypeScript bien fait. Les développeurs sont plus rapides en feature ajoutée à code existant. Ces gains se voient en vélocité d’équipe et qualité.

as casting partout, c’est mauvais ?

Oui généralement. Chaque as est une affirmation non vérifiée — vous prenez la responsabilité de dire au compilateur que vous savez mieux. Parfois nécessaire, mais à minimiser. Préférer narrowing (if (typeof x === ...)), type guards (function isClient(x: unknown): x is Client), validation à l’entrée (Zod, Yup) pour avoir des types validés plutôt que castés.


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é