JavaScript

TypeScript strict mode: perché attivarlo da subito è una delle scelte migliori che puoi fare

"Faceva troppe storie" è la risposta che sento più spesso quando trovo strict mode disattivato. Ecco perché è l'esatto contrario di un buon motivo.

15 Giu 2026·8 min di lettura·Antonio Tufo
Schermata di codice TypeScript con errori del compilatore evidenziati in rosso su editor VS Code scuro

TypeScript strict mode è un insieme di controlli aggiuntivi attivabili nel 0.85em] font-mono">tsconfig.json con ["strict": true. Abilita opzioni come noImplicitAny, strictNullChecks e strictFunctionTypes, che rendono il compilatore molto più preciso nell'individuare errori prima ancora di eseguire il codice. In ogni progetto TypeScript moderno, attivarlo dall'inizio è la scelta che riduce di più i bug difficili da trovare in produzione.

C'è una riga che compare quasi sempre nel tsconfig.json di un progetto nuovo, spesso già attiva per default quando si genera il progetto con create-next-app o create vite: "strict": true. E c'è una cosa che ho imparato a fare, ogni volta che entro in una codebase che non ho creato io: vado a controllare se quella riga è ancora lì, oppure se qualcuno l'ha rimossa o commentata. L'ho trovata commentata abbastanza spesso da farmi chiedere il motivo. La risposta che ricevo più frequentemente è: "faceva troppe storie". In questo articolo ti spiego perché quella risposta è, di fatto, l'esatto contrario di un buon motivo per disattivarla.

Cosa fa realmente `"strict": true`

Il primo malinteso da chiarire è che "strict": true non è una singola opzione: è uno shorthand che attiva un gruppo di opzioni al suo interno. Nel 2026, con TypeScript 5.x, questo gruppo include:

  • strictNullChecks
  • strictFunctionTypes
  • strictBindCallApply
  • strictPropertyInitialization
  • noImplicitAny
  • noImplicitThis
  • alwaysStrict
  • useUnknownInCatchVariables

Ognuna di queste ha un effetto specifico sul comportamento del type checker, e la documentazione ufficiale di TypeScript le spiega tutte nel dettaglio, ma quella che produce il maggior numero di errori a breve termine, e dunque quella che spinge molti a capitolare, è noImplicitAny. Con questa opzione attiva, TypeScript rifiuta di inferire automaticamente any per i parametri di una funzione quando non riesce a dedurne il tipo dal contesto. In pratica, se scrivi una funzione e non specifichi il tipo dei parametri, invece di passarci sopra in silenzio, TypeScript ti avverte. E a molte persone questo sembra un ostacolo, proprio quando lo incontrano per la prima volta in un progetto di dimensioni reali.

Perché `any` sembra comodo (e perché smette di esserlo)

Quando inizi a lavorare con TypeScript partendo da una base JavaScript, il tipo any sembra una benedizione. Ti permette di fare quello che facevi prima senza dover fermarti a pensare ai tipi. Puoi passare qualsiasi valore a qualsiasi funzione, accedere a qualsiasi proprietà senza che il compilatore dica niente. In pratica, any disattiva il type checker per quella variabile specifica.

Il problema è che any si diffonde. Una funzione che riceve un parametro di tipo any restituisce spesso qualcosa che TypeScript non riesce a tipare correttamente, e quel risultato viene passato a un'altra funzione, che a sua volta restituisce qualcosa di non tipato, e così via. Nel giro di qualche settimana su un progetto attivo, ti ritrovi con zone intere della codebase in cui TypeScript non controlla nulla, dal momento che l'any si è propagato silenziosamente. A quel punto hai TypeScript installato, ma non stai usando TypeScript. Stai usando JavaScript con una cerimonia aggiuntiva.

Ho visto questa dinamica prendere forma in più di un progetto, e il punto critico arriva invariabilmente quando si fa un refactoring. Cambi il tipo di ritorno di una funzione, sposti una proprietà, rinomini un campo in un'interfaccia: con noImplicitAny attivo e i tipi definiti correttamente, l'IDE ti mostra immediatamente tutti i punti della codebase che dipendono da quella funzione e che ora devono essere aggiornati. Senza i tipi, o con any diffuso, non lo scopri fino a quando non si rompe qualcosa in produzione. E scoprirlo in produzione è sempre più costoso che scoprirlo in sviluppo.

Il beneficio che vale la pena di sopportare il costo iniziale

Di tutti i miglioramenti che lo strict mode introduce, quello che ha cambiato più concretamente il mio modo di scrivere codice è strictNullChecks. Con questa opzione attiva, TypeScript distingue tra un valore che può essere null o undefined e uno che non può esserlo. Senza strict mode, null e undefined sono sottotipi di ogni altro tipo, dunque puoi assegnare null a una stringa o a un numero senza che TypeScript ti fermi.

Questo sembra un dettaglio tecnico, ma nella pratica è uno dei bug più frequenti che esistono nel codice JavaScript: il famoso Cannot read properties of null (reading 'something') in console. Con strictNullChecks attivo, prima di poter accedere a una proprietà su un valore che potrebbe essere null, TypeScript ti obbliga a verificare che non lo sia. Non è una formalità, è un modo per rendere esplicita una classe di errori che altrimenti ti arriva solo a runtime.

Un esempio concreto: supponiamo che tu abbia una funzione che cerca un utente nel database e restituisce User | null. Senza strictNullChecks, puoi accedere a utente.nome direttamente senza alcun controllo, e il codice compila senza errori. Se per qualsiasi motivo la funzione restituisce null in produzione, hai un crash. Con strictNullChecks attivo, TypeScript rifiuta di compilare quella riga fino a quando non aggiungi un controllo esplicito: if (utente !== null) oppure usi l'optional chaining utente?.nome. Sei tu che decidi come gestire il caso null, non il runtime che lo gestisce per te con un errore.

Nei progetti su cui ho lavorato, questa sola opzione ha eliminato un'intera categoria di bug che prima richiedevano debug manuale e spesso emergevano soltanto in scenari di edge case che in sviluppo non si riproducevano facilmente. È lo stesso tipo di rigore che porto nei progetti di sviluppo software e gestionali che seguo, dove un errore non intercettato a compile time può significare un dato sbagliato in produzione.

`strictFunctionTypes` e il narrowing: perché la struttura dei tipi conta

Un'altra opzione che lo strict mode abilita, e che vale la pena capire anche solo concettualmente, è strictFunctionTypes. Quest'ultima cambia il modo in cui TypeScript verifica la compatibilità tra tipi di funzione, passando da un controllo bidirezionale (covariante e controvariante) a uno più rigoroso per i parametri.

In termini pratici, il caso che questa opzione risolve è quello delle funzioni di callback tipate in modo troppo permissivo. Senza strictFunctionTypes, TypeScript può accettare una funzione che si aspetta un tipo più specifico al posto di una che ne accetta uno più generico, aprendo la porta a errori a runtime che a prima vista sembrano impossibili guardando solo i tipi. Con l'opzione attiva, questa classe di incompatibilità viene catturata a compile time.

Il concetto di type narrowing, invece, è legato a come TypeScript restringe il tipo di una variabile all'interno di un blocco condizionale. Se dichiari una variabile come string | null e poi scrivi if (valore !== null), dentro quel blocco TypeScript sa che valore è soltanto string. Questo meccanismo diventa molto più utile quando strictNullChecks è attivo, dal momento che senza di lui la distinzione tra null e non-null non esiste di fatto nel type system.

Imparare a usare il narrowing in modo consapevole, combinato con lo strict mode, è uno di quei passaggi che separa chi usa TypeScript come JavaScript con i tipi da chi ne sfrutta davvero le capacità.

Come attivarlo in un progetto esistente senza impazzire

Se stai partendo da zero, l'unica cosa giusta da fare è attivare "strict": true nel tsconfig.json dal primo giorno. Avrai meno errori da affrontare subito, e il progetto crescerà con basi solide fin dall'inizio.

Il caso difficile è quello di un progetto esistente in JavaScript o TypeScript senza strict mode, in cui aggiungere "strict": true produce centinaia o migliaia di errori in una volta sola. Qui la tentazione di rimandare è comprensibile, ma c'è un approccio che funziona meglio della resa: la migrazione graduale.

TypeScript permette di attivare le singole opzioni dello strict mode una alla volta. Puoi iniziare aggiungendo soltanto "strictNullChecks": true, risolvere quegli errori nel tempo, poi aggiungere "noImplicitAny": true, e così via. Non è veloce, ma è un percorso sostenibile anche su codebase grandi, dal momento che ogni passo produce un miglioramento reale senza richiedere di bloccare tutto il resto del lavoro.

Un'altra strategia che ho usato con successo su un progetto ereditato è la combinazione di strict mode globale con commenti selettivi di soppressione per i file non ancora migrati. Attivi "strict": true, poi aggiungi // @ts-nocheck in cima ai file che non hai ancora migrato, e sistemi un file alla volta. Ogni file migrato viene rimosso dalla lista, e la codebase diventa progressivamente più sicura. L'importante è che il nuovo codice che scrivi ogni giorno rispetti lo strict mode fin dall'inizio, invece di continuare ad accumulare debito tecnico.

In entrambi i casi, l'aspetto importante è fare il lavoro in modo incrementale piuttosto che procrastinarlo indefinitamente. Dal momento che ogni settimana si aggiunge nuovo codice, più aspetti ad attivare strict mode, più diventa difficile farlo in seguito. Nei progetti di sviluppo siti web che seguo con Next.js, per esempio, questo passaggio è ancora più delicato quando si lavora con i React Server Components, dal momento che i tipi condivisi tra server e client devono restare coerenti lungo tutto l'albero dei componenti.

`useUnknownInCatchVariables`: l'opzione meno conosciuta che vale la pena capire

C'è un'opzione introdotta in TypeScript 4.4, abilitata da "strict": true, che in molti non conoscono perché è relativamente recente: useUnknownInCatchVariables. Con questa opzione attiva, la variabile catturata in un blocco catch ha tipo unknown invece di any.

Prima di questa opzione, il codice seguente compilava senza errori:

``typescript try { fetchData(); } catch (error) { console.log(error.message); // nessun errore TypeScript } ``

Il problema è che non c'è nessuna garanzia che error abbia una proprietà message. Gli errori in JavaScript possono essere qualsiasi cosa: un'istanza di Error, una stringa, un numero, un oggetto personalizzato. Con useUnknownInCatchVariables attivo, TypeScript ti obbliga a verificare di che tipo è error prima di accedere alle sue proprietà:

``typescript try { fetchData(); } catch (error) { if (error instanceof Error) { console.log(error.message); // qui TypeScript sa che è un Error } } ``

Questo cambiamento piccolo nella sintassi ha un impatto concreto sulla qualità della gestione degli errori, che in molti progetti è quest'ultima una delle aree con più bug difficili da tracciare. Un'applicazione che gestisce gli errori in modo esplicito è un'applicazione che si comporta in modo prevedibile anche nei casi di edge.

Il costo reale di non usarlo

Mi capita spesso di sentire che "TypeScript strict fa perdere tempo". È una posizione comprensibile in un certo senso: nella prima settimana su un nuovo progetto, o nelle prime ore di migrazione di una codebase esistente, il numero di errori da risolvere sembra enorme. Ma il paragone corretto non è "quanto tempo ci vuole per risolvere questi errori adesso" contro "quanto tempo ci voleva a ignorarli". Il paragone corretto è "quanto tempo ci vuole adesso" contro "quanto tempo ci vorrà a debuggare questi stessi problemi in produzione, spesso sotto pressione, senza il contesto fresco che hai adesso".

Nella mia esperienza, ogni ora spesa a definire tipi corretti e a risolvere gli errori di strict mode torna indietro con gli interessi nella fase di manutenzione. Ogni volta che mi sono trovato a fare un refactoring significativo su una codebase con strict mode attivo, ho avuto la fiducia di sapere che se tutto compila, quasi certamente funziona. Non sempre, perché i test restano necessari, ma la superficie di errori possibili si riduce in modo MISURABILE.

Ogni volta che me lo sono trovato disattivato, o con any diffuso ovunque, ho scoperto le incoerenze a runtime. Non sempre in produzione, ma spesso in momenti inopportuni: durante una demo, alla prima esecuzione in un ambiente diverso, nel codice scritto da un'altra persona che pensava di usare i tipi in un certo modo ma in realtà li usava in un altro.

Dove finisce lo strict mode e dove inizia il resto

Attivare "strict": true è il punto di partenza, non il punto di arrivo. Esistono opzioni aggiuntive che lo strict mode non abilita di default, ma che vale la pena valutare in base al progetto: noUncheckedIndexedAccess, che rende l'accesso agli array e agli oggetti indicizzati intrinsecamente opzionale, è una di quelle che consiglio di esplorare quando sei già a tuo agio con lo strict mode base. exactOptionalPropertyTypes, introdotta in TypeScript 4.4, è un'altra opzione che distingue tra una proprietà opzionale assente e una proprietà opzionale impostata esplicitamente a undefined. Entrambe aggiungono ulteriore precisione al type system, seppur richiedano un po' più di attenzione nella gestione dei tipi.

Ma questi sono passi successivi. Il primo, quello che cambia davvero la qualità del codice che scrivi, è soltanto quello di aprire il 0.85em] font-mono">tsconfig.json e assicurarti che "strict": true sia lì e che nessuno l'abbia commentata. Lo stesso principio di rigore vale quando si scrive JavaScript puro, per esempio scegliendo consapevolmente tra le [Web API native del browser e una libreria esterna: meno superficie di codice non tipato o non controllato, meno bug in produzione.

Se stai partendo un progetto questa settimana, fallo adesso. Se stai lavorando su una codebase esistente, inizia con "strictNullChecks": true e vai avanti un passo alla volta. Non aspettare il momento perfetto, dal momento che quel momento non arriva mai. Il codice che scrivi oggi con i tipi sbagliati è il codice che cercherai di capire tra sei mesi sotto pressione.

Domande frequenti

Cosa fa esattamente "strict": true nel tsconfig.json di TypeScript?

Abilita un gruppo di opzioni di controllo più rigorose: noImplicitAny (nessun tipo any implicito), strictNullChecks (null e undefined non sono assegnabili senza controllo), strictFunctionTypes (controllo covarianza/contravarianza), noImplicitThis, alwaysStrict e altre. L'effetto complessivo è un compilatore che cattura categorie di errori che in modalità non-strict passerebbero inosservati fino al runtime.

Perché tanti progetti TypeScript hanno strict mode disattivato?

Perché attivarlo su un progetto esistente non-strict genera immediatamente decine o centinaia di errori da correggere. Molti team lo disattivano per velocizzare la migrazione a TypeScript, con l'intenzione di riattivarlo "più avanti" — ma quel momento spesso non arriva mai. La soluzione per codebase esistenti è l'attivazione graduale opzione per opzione, non tutto in una volta.

Come si attiva strict mode gradualmente su una codebase esistente?

Invece di abilitare "strict": true tutto in una volta, abilita le singole opzioni una alla volta, partendo da noImplicitAny. Correggi tutti gli errori relativi, fai commit, poi passa alla successiva. Questo approccio rende il processo sostenibile anche su codebase grandi. Alcune opzioni come strictNullChecks richiedono più lavoro ma producono i benefici più significativi in termini di bug eliminati.

TypeScript strict mode rallenta il processo di sviluppo?

Nel breve periodo sì: bisogna gestire più errori di compilazione. Nel medio-lungo periodo no, anzi: riduce significativamente il tempo speso in debug, rende i refactoring più sicuri perché il compilatore segnala ogni utilizzo rotto di un'interfaccia modificata, e migliora la leggibilità del codice perché i tipi espliciti rendono le intenzioni del codice più chiare a chi legge in futuro.

Argomenti

typescript strict modetsconfig strictnoImplicitAny typescriptstrictNullChecks typescripttypescript configurazionetypescript 2026
AT

Antonio Tufo

Full-Stack Developer & Interaction Designer. Lavoro con startup e PMI italiane.

Chi sono

Tutti gli articoli

Torna al blog

Hai un progetto da realizzare?

Dalla consulenza al deploy: trasformo le idee in prodotti digitali che funzionano.

Parliamo del tuo progetto

Preferenze Cookie

Scegli i cookie non tecnici

Questo sito utilizza Google Analytics 4 per analizzare il traffico in forma aggregata. I cookie analitici vengono attivati solo con il tuo consenso e puoi modificare la scelta in qualsiasi momento.