Prima esprimevo questa idea in modo volutamente provocatorio: TypeScript è il linguaggio migliore per Codex. Oggi la formulerei con più precisione. Per il tipo di sviluppo frontend e full-stack di produzione che faccio, TypeScript è l’ambiente in cui Codex è più semplice da controllare, verificare e usare con fiducia.
La parola importante non è “TypeScript”, ma controllo. Gli agenti di coding basati su IA sono molto bravi a produrre codice plausibile. La produzione richiede qualcosa di più severo: il codice deve rispettare i contratti esistenti, preservare il comportamento, sopravvivere ai refactor e fallire presto quando un’ipotesi è sbagliata. TypeScript rende molte più di queste restrizioni leggibili dalla macchina rispetto al JavaScript puro.
Di cosa ha davvero bisogno Codex da una codebase
Un buon prompt aiuta, ma è solo una fonte di contesto. La codebase stessa può rendere il compito facile da capire oppure costringere il modello a indovinare. In pratica, l’ambiente migliore è quello in cui le assunzioni importanti sono visibili e verificate automaticamente.
- Le firme delle funzioni definiscono input e output ammessi.
- Interfaces e types rendono esplicita la forma degli oggetti di dominio.
- Union ed enum restringono l’insieme degli stati validi.
- Il compilatore trasforma molte ipotesi sbagliate in errori concreti subito dopo una modifica.
Un agente IA non deve comprendere perfettamente l’intera applicazione se l’applicazione stessa reagisce quando viene violato un contratto. Più breve è questo feedback loop, meno spazio resta alle supposizioni sicure di sé.
I tipi sono contesto eseguibile, non semplice documentazione
Un buon TypeScript documenta l’intento per le persone, ma in un workflow automatizzato ha un valore in più: quella documentazione è verificabile. Per esempio:
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.
}In JavaScript lo stesso errore può sembrare ragionevole finché quel ramo non viene eseguito a runtime. TypeScript può rifiutare l’ipotesi prima del merge. Per Codex, l’errore del compilatore non è soltanto una riga rossa nell’editor: è un segnale preciso di ciò che il modello ha frainteso.
Per questo preferisco tipi di dominio espliciti a strutture molto larghe come Record<string, any>. Più i tipi descrivono bene la realtà, più la codebase diventa un contesto utile sia per gli sviluppatori sia per l’IA.
Il compilatore trasforma gli errori in un feedback loop
Non mi fido del processo “chiedi il codice a Codex e accetta la prima risposta”. Il workflow è iterativo:
task → inspect → edit → typecheck → lint → test → review diffÈ una sequenza noiosa, ed è proprio per questo che funziona. Dopo ogni cambiamento significativo, l’ambiente risponde. Gli errori di tipo intercettano contratti rotti. Il lint trova un’altra classe di problemi. I test verificano comportamenti che i tipi non possono esprimere. Il diff finale riceve comunque una review umana.
Un prompt intelligente può migliorare il primo tentativo. Un forte ciclo di verifica migliora ogni tentativo. In produzione conta molto di più.
I grandi refactor sono il punto in cui TypeScript vale di più
Le modifiche piccole e isolate sono semplici per quasi ogni assistente. Il caso difficile è una modifica che si propaga su decine di file: rinominare un campo di dominio, cambiare una risposta API, restringere le props di un componente, dividere una union o sostituire una vecchia astrazione.
Con TypeScript, una breaking change crea una mappa delle assunzioni toccate. Il compilatore mostra dove viene ancora usato il vecchio contratto. Codex può attraversare quella lista e rieseguire i controlli a ogni passaggio. Non rende il refactor automaticamente corretto, ma rende visibile il blast radius.
In una codebase poco tipizzata, una dipendenza dimenticata può comparire per la prima volta come bug in una schermata che nessuno ha aperto durante lo sviluppo.
Dove TypeScript non può salvarti
TypeScript è un guardrail, non una prova di correttezza. Non può prevenire categorie importanti di errori:
- Logica di business sbagliata: codice perfettamente tipizzato può comunque calcolare la cosa sbagliata.
- Input a runtime: API, database, form o servizi terzi possono restituire dati diversi dalle assunzioni di compile time.
- Contesto prodotto mancante: il compilatore non sa perché esiste un flusso o quale edge case conta per l’utente.
- Errori di sicurezza e architettura: tipi validi non garantiscono autorizzazione corretta, caching, confini del database o infrastruttura sensata.
Runtime validation, test, logging, observability e review restano quindi necessari. Un typecheck pulito non equivale a una feature corretta.
Il workflow che uso per i cambiamenti assistiti dall’IA
- Definisco un obiettivo stretto. Cosa deve cambiare, cosa non deve cambiare e quali sono i criteri di accettazione.
- Faccio ispezionare il progetto prima di modificare. Tipi esistenti, call site, test e moduli vicini spesso valgono più di un prompt lungo.
- Preferisco il più piccolo cambiamento coerente. Un diff focalizzato è più semplice da verificare di una riscrittura “utile” di codice non correlato.
- Eseguo subito typecheck e lint. Voglio che gli errori strutturali emergano quando la modifica è ancora piccola.
- Eseguo i test rilevanti e ne aggiungo se cambia il comportamento. I tipi validano i contratti; i test validano il comportamento.
- Rivedo manualmente il diff finale. Naming, architettura, duplicazioni, edge case e la soluzione reale del problema rimangono da controllare.
Quando JavaScript è ancora sufficiente
Questo non significa che JavaScript sia cattivo. Per un piccolo script, un prototipo usa-e-getta, una semplice automazione o codice con poco stato condiviso, la struttura aggiuntiva di TypeScript può offrire poco. Anche un progetto JavaScript con test eccellenti e convenzioni chiare può essere un ottimo ambiente per l’IA.
La mia affermazione è più limitata: più una codebase diventa grande, longeva e collaborativa, più aumenta il valore delle restrizioni verificabili dalla macchina. Ed è anche il momento in cui gli errori prodotti dall’IA diventano più costosi.
Il vero motivo per cui preferisco TypeScript con Codex
TypeScript non rende Codex più intelligente. Rende l’ambiente meno permissivo verso le cattive supposizioni.
In produzione non voglio soltanto un agente creativo; voglio un agente dentro un sistema che gli dica continuamente quando sta sbagliando. Tipi, errori del compilatore, lint, test e review umana costruiscono insieme quel sistema.
Quindi, quando dico che TypeScript funziona eccezionalmente bene con Codex, non è fanatismo per un linguaggio. È una scelta a favore di feedback rapido e verificabile dalla macchina: meno ambiguità, blast radius più piccolo, refactor più sicuri e, se la verifica viene presa sul serio, una velocità di engineering molto più affidabile.