J’avais l’habitude de formuler l’idée de manière volontairement provocatrice : TypeScript est le meilleur langage pour Codex. Aujourd’hui, je serais plus précis. Pour le type de développement frontend et full-stack en production que je pratique, TypeScript est l’environnement dans lequel Codex est le plus simple à contrôler, vérifier et utiliser avec confiance.
Le mot important n’est pas « TypeScript », mais contrôle. Les agents de code IA savent très bien produire du code plausible. L’ingénierie de production demande davantage : respecter les contrats existants, préserver le comportement, survivre aux refactorings et échouer rapidement lorsqu’une hypothèse est fausse. TypeScript rend beaucoup plus de ces contraintes lisibles par la machine que JavaScript seul.
Ce dont Codex a réellement besoin dans une codebase
Un bon prompt aide, mais il ne représente qu’une source de contexte. La codebase elle-même peut faciliter le raisonnement ou obliger le modèle à deviner. En pratique, le meilleur environnement est celui où les hypothèses importantes sont visibles et automatiquement vérifiées.
- Les signatures de fonctions définissent les entrées et sorties autorisées.
- Les interfaces et les types rendent explicite la forme des objets métier.
- Les unions et enums réduisent l’ensemble des états valides.
- Le compilateur transforme de nombreuses mauvaises hypothèses en erreurs concrètes immédiatement après l’édition.
Un agent IA n’a pas besoin de comprendre toute l’application parfaitement si l’application sait réagir lorsqu’il viole un contrat. Plus cette boucle de retour est courte, moins il reste de place aux suppositions trop confiantes.
Les types sont du contexte exécutable, pas seulement de la documentation
Un bon TypeScript documente l’intention pour les humains. Dans un workflow automatisé, il va plus loin : cette documentation est testable. Par exemple :
type PaymentResult =
| { ok: true; receiptId: string }
| { ok: false; code: "declined" | "timeout" };
function getReceiptId(result: PaymentResult) {
if (result.ok) return result.receiptId;
return result.receiptId;
// TypeScript: Property 'receiptId' does not exist
// on the failure branch.
}En JavaScript, la même erreur peut sembler raisonnable jusqu’au passage de cette branche à l’exécution. TypeScript peut rejeter l’hypothèse avant le merge. Pour Codex, l’erreur du compilateur n’est donc pas seulement une ligne rouge dans l’éditeur, mais un signal précis sur ce qui a été mal compris.
C’est pourquoi je préfère des types métier explicites aux constructions très larges comme Record<string, any>. Plus les types décrivent fidèlement la réalité, plus la codebase devient un contexte utile pour les développeurs comme pour l’IA.
Le compilateur transforme les erreurs en boucle de feedback
Je ne fais pas confiance au processus « demander du code à Codex et accepter la première réponse ». Le workflow est itératif :
task → inspect → edit → typecheck → lint → test → review diffC’est une séquence peu spectaculaire, et c’est précisément sa force. Après chaque changement significatif, l’environnement répond. Les erreurs de types trouvent les contrats cassés. Le lint couvre une autre classe de problèmes. Les tests vérifient des comportements que les types ne peuvent pas exprimer. Le diff final reste soumis à une revue humaine.
Un prompt habile peut améliorer le premier essai. Une bonne boucle de vérification améliore chaque essai. Pour la production, c’est beaucoup plus important.
Les gros refactorings sont l’endroit où TypeScript prend tout son sens
Les petites modifications isolées sont simples pour presque tous les assistants. Le cas difficile est une modification qui se propage dans des dizaines de fichiers : renommer un champ métier, changer une réponse API, durcir des props, scinder une union ou remplacer une ancienne abstraction.
Avec TypeScript, un breaking change crée une carte des hypothèses touchées. Le compilateur montre où l’ancien contrat est encore utilisé. Codex peut parcourir cette liste, puis relancer la vérification après chaque passe. Cela ne rend pas le refactoring automatiquement correct, mais rend son blast radius visible.
Dans une codebase peu typée, une dépendance oubliée peut n’apparaître qu’en production sous forme de bug sur un écran que personne n’a ouvert pendant le développement.
Ce que TypeScript ne peut pas garantir
TypeScript est un garde-fou, pas une preuve de correction. Il ne prévient pas plusieurs catégories d’échecs :
- Mauvaise logique métier : du code parfaitement typé peut produire un mauvais résultat.
- Données runtime : API, base de données, formulaire ou service tiers peuvent retourner des données différentes des hypothèses de compilation.
- Contexte produit manquant : le compilateur ne sait pas pourquoi un parcours existe ni quel edge case compte pour l’utilisateur.
- Sécurité et architecture : des types valides ne garantissent ni bonne autorisation, ni caching correct, ni frontières de données ou infrastructure saines.
Runtime validation, tests, logs, observability et revue restent donc nécessaires. Un typecheck propre ne prouve pas qu’une fonctionnalité est correcte.
Le workflow que j’utilise pour les changements assistés par IA
- Définir un objectif étroit. Ce qui doit changer, ce qui ne doit pas changer et les critères d’acceptation.
- Faire inspecter le projet avant l’édition. Types, call sites, tests et modules voisins donnent souvent plus de contexte qu’un long prompt.
- Préférer le plus petit changement cohérent. Un diff ciblé est plus facile à vérifier qu’une réécriture « utile » de code non concerné.
- Lancer typecheck et lint immédiatement. Les erreurs structurelles doivent apparaître tant que le changement est encore petit.
- Lancer les tests pertinents et en ajouter si le comportement change. Les types valident les contrats, les tests valident le comportement.
- Relire manuellement le diff final. Nommage, architecture, duplication, edge cases et résolution réelle du problème restent à vérifier.
Quand JavaScript suffit encore largement
Ce n’est pas une attaque contre JavaScript. Pour un petit script, un prototype jetable, une automatisation simple ou du code avec très peu d’état partagé, le coût structurel de TypeScript peut ne pas être justifié. Un projet JavaScript avec d’excellents tests et des conventions claires peut également être un très bon environnement pour l’IA.
Mon propos est plus limité : plus une codebase devient grande, durable et collaborative, plus les contraintes vérifiables par machine prennent de la valeur. Et c’est justement là que les erreurs générées par l’IA deviennent plus coûteuses.
La vraie raison pour laquelle je préfère TypeScript avec Codex
TypeScript ne rend pas Codex plus intelligent. Il rend l’environnement moins tolérant aux mauvaises suppositions.
En production, je ne veux pas seulement un agent créatif ; je veux un agent dans un système qui lui dit continuellement quand il se trompe. Types, erreurs du compilateur, lint, tests et revue humaine forment ensemble ce système.
Donc, quand je dis que TypeScript fonctionne particulièrement bien avec Codex, je ne parle pas de fanatisme de langage. Je parle de feedback rapide et vérifiable par la machine : moins d’ambiguïté, un blast radius plus faible, des refactorings plus sûrs et, si la vérification est prise au sérieux, une vitesse d’ingénierie beaucoup plus fiable.