Migrer de JavaScript à TypeScript avec l’IA : un plan sûr

Migrer JavaScript vers TypeScript avec des agents IA : tsconfig qui accepte le JS, ordre fichier par fichier, typage durci peu à peu, budget de any et tsc.

Solviera Teknoloji 15 min de lecture

Pour migrer de JavaScript à TypeScript avec l’IA, on ne demande pas à un agent de « convertir le projet ». On met en place une config TypeScript qui accepte le JavaScript existant, puis on laisse les agents convertir les fichiers dans un ordre fixe, le vérificateur de types et les tests décidant quand chaque étape est terminée. Le compilateur est le meilleur relecteur que vous puissiez donner à un agent : il est rapide, ne se fatigue jamais et ne se laisse pas convaincre que « ça a l’air bon ».

Ce guide est la version pratique de cette idée : le tsconfig.json exact pour démarrer, comment choisir l’ordre des fichiers, comment durcir le typage sans une semaine de builds rouges, comment garder any sous contrôle, comment faire de la vérification de types l’étape de contrôle de l’agent lui-même et comment répartir le travail entre agents parallèles sans conflits de fusion. Pour le filet de sécurité général d’un refactoring de code legacy (tests de caractérisation, retour arrière, revue), lisez d’abord refactoriser du code legacy avec l’IA ; cet article ne le répète pas.

Les détails qui dépendent de la version suivent la documentation officielle de TypeScript d’octobre 2026.

Pourquoi les agents sont doués pour ce travail

Une migration de JS vers TS est surtout un travail mécanique, avec un signal clair à la fin de chaque étape. C’est exactement ce que les agents de code font bien :

  • Le retour est immédiat. tsc affiche le fichier, la ligne et le code d’erreur. Un agent peut le lire et corriger sans deviner.
  • Le travail se découpe naturellement. Fichiers et dossiers peuvent être convertis un par un, si bien que le chantier se divise en nombreuses petites étapes faciles à relire.
  • La partie ennuyeuse est énorme. Ajouter des types de paramètres, écrire des interfaces pour des objets qui existent déjà et installer des paquets @types : c’est le genre de travail que l’on repousse pendant des années.

Le risque est tout aussi clair. Un agent pressé de faire passer le compilateur au vert ira chercher any, des casts as et @ts-ignore, parce qu’ils font disparaître les erreurs. L’essentiel de ce guide consiste à fermer ces sorties de secours.

Étape 1 : un tsconfig qui accepte votre JavaScript

Le premier commit ne doit modifier aucun code applicatif. Il ajoute TypeScript et une config qui vérifie les types du projet alors que tous les fichiers sont encore en .js.

npm install --save-dev typescript @types/node
{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": false,
    "strict": false,
    "noEmit": true,
    "incremental": true,
    "target": "es2022",
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "types": ["node"],
    "skipLibCheck": true
  },
  "include": ["src"]
}

Ce que fait chaque choix :

  • allowJs permet aux fichiers .js et .ts de cohabiter dans le même programme : un fichier .ts peut importer un fichier .js, et inversement.
  • checkJs: false empêche TypeScript de signaler des erreurs dans les fichiers que vous n’avez pas encore touchés. Vous les activerez un par un.
  • strict: false est volontaire. Depuis TypeScript 6.0, strict vaut true par défaut ; l’omettre activerait donc toutes les vérifications strictes dès le premier jour.
  • types: ["node"] compte pour la même raison : dans TypeScript 6.0, types est un tableau vide par défaut, donc les globales de Node ne sont pas prises en compte si vous ne les listez pas. Ajoutez "jest" ou "vitest/globals" si vos tests reposent sur des globales.
  • noEmit convient aux projets où un bundler, tsx ou un framework transforme déjà le code en JavaScript et où TypeScript ne fait que vérifier. Si tsc construit votre application, retirez-le et définissez plutôt outDir.
  • module et moduleResolution : utilisez nodenext pour les applications Node ; pour du code construit par un bundler, "module": "esnext" avec "moduleResolution": "bundler" est en général la bonne paire.

Ajoutez un script et faites-le lancer par la CI :

{
  "scripts": {
    "typecheck": "tsc -p tsconfig.json"
  }
}

Lancez-le une fois. Sur une base de code JavaScript avec checkJs désactivé, il devrait passer ou n’afficher que quelques erreurs de config. Corrigez-les, commitez, et vous avez une base de référence. À partir de là, « le build est vert » signifie que npm run typecheck et npm test passent tous les deux.

Étape 2 : prévisualiser les erreurs avec @ts-check avant de renommer

Avant de renommer un fichier, vous pouvez voir ce que TypeScript en pense. Ajoutez // @ts-check en première ligne d’un fichier .js et TypeScript y signale les erreurs, en utilisant les commentaires JSDoc comme types :

// @ts-check

/**
 * @param {number} cents
 * @param {string} currency
 * @returns {string}
 */
export function formatPrice(cents, currency) {
  return new Intl.NumberFormat('en', { style: 'currency', currency }).format(cents / 100);
}

C’est une bonne première tâche pour un agent sur un gros fichier : « Ajoute // @ts-check à src/orders/pricing.js, corrige les erreurs uniquement avec des types JSDoc, ne change pas le comportement. » Vous obtenez la sûreté des types sans renommage, et la conversion ultérieure devient presque mécanique. Si vous préférez l’approche inverse, mettez checkJs: true et ajoutez // @ts-nocheck aux fichiers qui ne sont pas encore prêts ; retirer cette ligne devient alors l’étape.

Étape 3 : choisir l’ordre, les feuilles d’abord

Convertissez d’abord les fichiers qui n’importent rien de votre propre code, puis ceux qui n’importent que ceux-là, et ainsi de suite jusqu’aux points d’entrée. Ainsi, chaque fichier converti peut s’appuyer sur des types qui existent déjà.

Inutile de déterminer cet ordre à la main. madge lit le graphe des imports :

npx madge --leaves --extensions js,jsx src/
npx madge --circular --extensions js,jsx src/

--leaves liste les modules sans dépendances, et --circular liste les cycles d’imports. Mieux vaut connaître les cycles dès le départ : les fichiers d’un cycle doivent être convertis ensemble, prévoyez-les donc comme une seule étape.

Une règle de découpage pratique : une étape correspond soit à un gros fichier, soit à un dossier de petits fichiers, assez petite pour que le diff se relise d’une traite. Mettez la liste dans un fichier versionné comme MIGRATION.md, une ligne par étape, et laissez les agents la cocher. Ce fichier devient le plan commun de tous les agents et de toutes les sessions.

Étape 4 : un seul prompt de conversion, une seule définition de « terminé »

Utilisez la même forme de prompt pour chaque étape, afin que les agents n’inventent pas leurs propres règles :

Convert src/billing/ from JavaScript to TypeScript.

- Rename with git mv so history follows the files (.js -> .ts, .jsx -> .tsx).
- Add types for parameters, return values and exported objects.
  Prefer types inferred from real usage over wide ones.
- Don't change runtime behaviour. No refactoring, no renaming,
  no "while I'm here" fixes. If you find a bug, note it in
  MIGRATION.md under "Found during migration" and leave it.
- No @ts-ignore, no @ts-nocheck. @ts-expect-error only with a reason
  on the same line. No new `any` beyond the budget in .any-budget.
- Don't edit tsconfig.json, package.json or src/types/.
  If you need a change there, stop and tell me what and why.

Done means: npm run typecheck passes, npm test passes,
npm run lint passes, and MIGRATION.md is updated.

Deux lignes de ce prompt font l’essentiel du travail. « Ne change pas le comportement à l’exécution » garde le diff relisible, car une migration de types pure ne devrait ajouter que des annotations, des renommages et des imports. « Ne modifie pas tsconfig.json » bloque le chemin le plus facile vers le vert : assouplir le compilateur.

Mettez les parties stables de ce prompt dans CLAUDE.md ou AGENTS.md pour que chaque session démarre avec elles ; CLAUDE.md et AGENTS.md explique comment partager un même ensemble de règles entre agents.

Étape 5 : un budget de any qui ne fait que baisser

Vous n’arriverez pas à zéro any en une passe, et l’interdire purement et simplement produit souvent de pires échappatoires, comme as unknown as Foo. Un budget est plus honnête : vous autorisez un nombre, et ce nombre ne fait que baisser.

Commencez par rendre visibles any et les commentaires de suppression avec typescript-eslint :

// eslint.config.js (excerpt)
rules: {
  '@typescript-eslint/no-explicit-any': 'warn',
  '@typescript-eslint/ban-ts-comment': ['error', {
    'ts-expect-error': 'allow-with-description',
    'ts-ignore': true,
    'ts-nocheck': true,
    minimumDescriptionLength: 10,
  }],
}

Avec ces options, ban-ts-comment rejette @ts-ignore et @ts-nocheck et n’accepte @ts-expect-error qu’avec une raison écrite d’au moins dix caractères. @ts-expect-error est de toute façon la meilleure échappatoire : une fois l’erreur corrigée, le commentaire lui-même devient une erreur, il ne peut donc pas rester périmé.

Comptez ensuite les any explicites et comparez-les à un nombre stocké dans le dépôt :

#!/usr/bin/env bash
# scripts/any-budget.sh
count=$(npx eslint src --format json \
  | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{const r=JSON.parse(s);console.log(r.flatMap(f=>f.messages).filter(m=>m.ruleId==="@typescript-eslint/no-explicit-any").length)})')
budget=$(cat .any-budget)
echo "explicit any: $count (budget $budget)"
[ "$count" -le "$budget" ] || { echo "any budget exceeded"; exit 1; }

Lancez-le en CI et intégrez-le à la définition de « terminé » de l’agent. Quand une étape fait baisser le compte, abaissez .any-budget dans le même commit. Dites à l’agent où se trouve le budget : les agents respectent un nombre bien mieux qu’un « évite any autant que possible ».

Surveillez les autres échappatoires, que le compte ne voit pas : les casts as sur des valeurs venues de l’extérieur (JSON.parse, corps de requêtes), Function et object comme types de paramètres, et une ligne declare module '*' qui transforme chaque import en any. Demandez au relecteur, humain ou agent, de lister chaque nouveau cast dans le diff.

Étape 6 : faire de la vérification de types l’étape de contrôle de l’agent

Un agent qui dit « terminé » sans avoir lancé tsc devine. Faites de la vérification une partie de la façon dont l’agent termine un tour, pas quelque chose que vous devez penser à demander.

Dans Claude Code, un hook Stop peut refuser la fin du tour tant que la vérification de types échoue. Enregistrez ceci sous .claude/hooks/typecheck.sh :

#!/usr/bin/env bash
# Exit 2 sends stderr back to Claude and keeps it working.
input=$(cat)
if [ "$(echo "$input" | jq -r '.stop_hook_active')" = "true" ]; then
  exit 0   # already retried once; let the turn end
fi
npx tsc -p tsconfig.json --pretty false 1>&2 || exit 2

puis déclarez-le dans .claude/settings.json :

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          { "type": "command", "command": ".claude/hooks/typecheck.sh" }
        ]
      }
    ]
  }
}

Quand un hook Stop se termine avec le code 2, Claude Code ne s’arrête pas et montre la sortie stderr du hook à Claude : l’agent voit les erreurs du compilateur et continue de corriger. La vérification de stop_hook_active laisse le tour se terminer au deuxième essai au lieu de boucler. Le guide des hooks de Claude Code approfondit les hooks. Un hook PostToolUse sur Edit|Write peut lancer une vérification plus rapide après chaque modification, mais sur de gros projets un tsc complet à chaque modification devient lent.

Codex et Gemini CLI ne lisent pas les hooks de Claude : mettez donc la même règle dans AGENTS.md ou GEMINI.md : « Avant de dire qu’une tâche est terminée, lance npm run typecheck et npm test et colle les dernières lignes de la sortie. » C’est le fait de demander la sortie qui rend la consigne efficace.

Étape 7 : durcir le typage une option à la fois

Une fois que tous les fichiers sont en .ts, ou qu’une zone l’est, activez les vérifications strictes dans cet ordre, avec un commit et une tâche d’agent séparés pour chacune :

  1. noImplicitAny : chaque paramètre et chaque variable doivent avoir un type connu. C’est l’option qui produit le plus d’erreurs et les types les plus utiles.
  2. strictNullChecks : null et undefined deviennent des types à part entière. Celle-ci trouve de vrais bugs, du genre « cannot read property of undefined ».
  3. Le reste de la famille strict : strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, noImplicitThis, useUnknownInCatchVariables. En général moins d’erreurs pour chacune.
  4. "strict": true remplace les options individuelles une fois qu’elles passent toutes.
  5. Des extras optionnels qui ne font pas partie de strict : noUncheckedIndexedAccess (les lectures indexées peuvent valoir undefined) et exactOptionalPropertyTypes.

Vous n’êtes pas obligé d’activer une option pour tout le dépôt d’un coup. Une seconde config qui étend la première et ne couvre que les dossiers terminés permet au code strict de rester strict pendant que le reste rattrape son retard :

{
  "extends": "./tsconfig.json",
  "compilerOptions": { "strict": true },
  "include": ["src/billing", "src/utils"]
}

Enregistrez-la sous tsconfig.strict.json et lancez tsc -p tsconfig.strict.json en CI à côté de la vérification principale. Chaque fois qu’un agent termine un dossier, il l’ajoute à include. Quand include couvre src, déplacez strict: true dans la config principale et supprimez le second fichier.

Une mise en garde pour strictNullChecks : la correction la plus rapide pour « possibly undefined » est l’assertion non nulle !. Quelques-unes, ça va ; des dizaines signifient que l’agent cache le problème. Demandez plutôt une garde, une valeur par défaut ou un changement de type, et traitez chaque nouveau ! comme un nouveau any pendant la revue.

Étape 8 : répartir le travail entre agents parallèles

Une migration est l’un des rares travaux où faire tourner plusieurs agents à la fois rapporte vraiment, car les dossiers sont en grande partie indépendants. Pour que cela marche, découpez par dossier et donnez à chaque agent son propre git worktree :

git worktree add ../app-ts-billing -b ts/billing
git worktree add ../app-ts-orders  -b ts/orders
git worktree add ../app-ts-users   -b ts/users

Lancez un agent dans chaque dossier avec le prompt d’étape ci-dessus, pointé sur son répertoire. Quelques règles évitent que les branches se télescopent :

  • Les fichiers partagés ont un seul propriétaire. tsconfig.json, package.json, le lockfile, eslint.config.js et les déclarations partagées de src/types/ ne changent que sur la branche principale, par vous ou par un seul agent coordinateur. Les agents de dossier demandent ces changements au lieu de les faire.
  • Les types partagés passent en premier. Si plusieurs dossiers ont besoin du même type User ou Order, faites-en une étape séquentielle avant le début des étapes parallèles. Sinon, trois agents écrivent trois interfaces User légèrement différentes.
  • Installez les paquets @types de façon centralisée. Deux branches qui ajoutent chacune un paquet différent réécrivent le lockfile et entrent en conflit à chaque fois.
  • Ne parallélisez pas un cycle. Des dossiers qui s’importent en boucle reviennent à un seul agent.
  • Fusionnez une branche à la fois et vérifiez les types après chaque fusion. Deux branches peuvent chacune passer tsc et échouer ensemble, par exemple quand l’une modifie un type exporté dont l’autre dépend. Rebasez la branche suivante sur le nouveau main avant de la fusionner.

Git worktrees pour agents en parallèle détaille la mise en place et le nettoyage. Trois ou quatre agents sont un bon début ; au-delà, c’est généralement la relecture des branches qui devient le goulot d’étranglement, pas leur écriture.

Étape 9 : laisser les tests prouver que rien n’a changé

Les types vous disent que le code est cohérent avec lui-même. Seuls les tests vous disent qu’il fait toujours ce qu’il faisait. Pendant une migration :

  • Lancez toute la suite après chaque étape, pas seulement les tests du dossier converti. Un export modifié peut casser un appelant ailleurs.
  • Convertissez les fichiers de test après le code qu’ils testent, dans une étape séparée, et ne modifiez pas les assertions pendant la conversion. Un test dont la valeur attendue a changé dans un commit « types uniquement » est un signal d’alarme.
  • Vérifiez que le lanceur de tests gère .ts. Vitest exécute les fichiers TypeScript sans configuration supplémentaire ; Jest a besoin d’un transform comme ts-jest ou babel-jest avec le preset TypeScript.
  • Ajoutez des tests là où les types ne vont pas. Le code qui analyse des entrées, parle à une API ou lit des variables d’environnement reçoit à l’exécution des données de forme any, quoi que disent les types. Quelques tests à ces endroits valent plus que des annotations parfaites. Écrire des tests unitaires avec l’IA montre comment obtenir des agents des tests utiles.

Pièges courants

  • Convertir require en import change la sémantique. Les exports par défaut, module.exports = fn et l’interopérabilité diffèrent entre CommonJS et les modules ES. Faites du changement de format de module une étape à part, ou laissez-le complètement hors de la migration.
  • Des types qui mentent. Une interface écrite à partir d’un seul objet d’exemple peut manquer des champs qui n’apparaissent que parfois. Préférez des types écrits à partir de données réelles ou du schéma de l’API.
  • Des « corrections » de comportement cachées dans un commit de types. Un == devenu ===, un paramètre par défaut ajouté, un if réordonné. Chacun peut changer le comportement. Refusez-les et demandez un commit séparé.
  • Faire taire le compilateur par la config. skipLibCheck, ça va ; désactiver une option stricte pour faire passer une branche, non. C’est pour cela que les agents n’ont pas le droit de modifier tsconfig.json.
  • Fichiers générés ou vendorisés. Excluez-les dans tsconfig.json plutôt que de laisser un agent les « typer ».

Le faire dans AgentVera

AgentVera fait tourner Claude Code, Codex, Gemini CLI et d’autres agents en CLI côte à côte par projet, ce qui convient bien à une migration découpée en nombreuses petites étapes.

  • Un worktree par dossier. Depuis le panneau Git, créez un worktree pour chaque dossier et lancez-y un agent en un clic, Claude Code sur un dossier et Codex sur un autre si vous le souhaitez.
  • Propriété des fichiers. Confiez tsconfig.json, package.json et src/types/** à l’agent coordinateur. Tant qu’il tourne, les modifications de ces fichiers par les autres agents sont refusées avant d’avoir lieu, et on leur dit de demander au propriétaire ; les agents Claude Code sont arrêtés par un hook, les autres CLI reçoivent la règle dans leurs instructions.
  • Verify. Écrivez « la vérification de types passe, les tests passent, aucune assertion de test modifiée » dans votre message. À la fin du tour, un vérificateur distinct qui ne peut pas modifier de fichiers lance les commandes de test autorisées, lit le diff et marque chaque critère réussi ou échoué, avec des preuves.
  • Revue de code. Un second modèle lit chaque tour terminé et liste ses constats avec fichier:ligne, ce qui est un bon endroit pour repérer les nouveaux casts et les assertions !.
  • File de fusion. Les branches de dossier arrivent une par une sur le dernier main, et votre commande de vérification, par exemple npm run typecheck && npm test, tourne sur le résultat fusionné avant toute fusion.
  • Parallel team (Pro et Team). Un agent peut découper la migration en huit parties au plus, avec les fichiers que chaque partie peut toucher, faire tourner chaque partie dans son propre worktree, fusionner les parties terminées dans une branche d’intégration et lancer la vérification finale : vous relisez une seule pull request.

Pour conclure

Migrer de JavaScript à TypeScript avec l’IA fonctionne quand ce sont le compilateur et les tests qui commandent, pas l’assurance de l’agent. Partez d’une config qui accepte JavaScript et met strict: false exprès, convertissez les feuilles d’abord par petites étapes, gardez any dans un budget qui ne fait que baisser, faites de tsc le contrôle de sortie de l’agent, durcissez le typage option par option, et répartissez le travail parallèle par dossier, les fichiers partagés appartenant à un seul agent. Chaque étape est ennuyeuse et facile à relire, et c’est exactement ce que l’on attend d’une migration.

Si vous suivez les agents pas à pas pendant la migration, le pair programming avec l’IA explique quand laisser l’agent conduire et quand relire chaque étape.

Questions

L’IA peut-elle convertir un projet JavaScript en TypeScript ?

Oui, et c’est l’un des travaux que les agents font bien, à condition de leur donner un vérificateur de types qui fonctionne, un ordre fixe et de petites étapes. Laissez l’agent convertir un fichier ou un dossier à la fois, lancer tsc et les tests après chaque étape, et commiter chaque étape séparément.

Faut-il activer le mode strict dès le départ ?

Pas dans une base de code existante. Depuis TypeScript 6.0, strict est activé par défaut : mettez donc explicitement "strict": false au début, convertissez les fichiers, puis activez noImplicitAny, strictNullChecks et les autres options strictes une par une.

Comment empêcher l’agent de mettre des any partout ?

Rendez-le mesurable. Signalez les any explicites avec la règle typescript-eslint no-explicit-any, interdisez @ts-ignore avec ban-ts-comment, gardez un budget chiffré dans le dépôt et faites échouer la CI quand le compte le dépasse. Indiquez ce budget à l’agent dans ses instructions.

Plusieurs agents peuvent-ils migrer des dossiers différents en même temps ?

Oui, si chacun travaille dans son propre git worktree et possède un seul dossier. Les fichiers partagés comme tsconfig.json, package.json et les déclarations de types globales doivent appartenir à un seul agent, et chaque branche doit être revérifiée par le compilateur après sa fusion.

Faut-il réécrire les tests pendant la migration ?

Non. Gardez d’abord les tests existants sur le code converti : ce sont eux qui vous disent que le comportement n’a pas changé. Convertissez les fichiers de test après le module qu’ils couvrent, et évitez de modifier les assertions dans la même étape.

Réunissez vos agents sur un seul bureau.

Téléchargez AgentVera gratuitement ; vos CLI installées sont prêtes à l’emploi.

Autres articles