Salta al contenuto principale

Strict Mode non Basta: 100 Breach di Tipo in un Monorepo TypeScript al 100% Strict

strict, strictTemplates e strictInjectionParameters tutti attivi, eppure 54 ': any' e 46 'as any' sopravvivono su 3884 file. Perché strict mode elimina solo gli errori accidentali, non le vie di fuga deliberate, e cosa fare davvero con gli any residui.

Cosa ne pensi di questo articolo?

Strict Mode non Basta: 100 Breach di Tipo in un Monorepo TypeScript al 100% Strict
TypeScript Avanzatofedericocalo.dev

In Breve

strict, strictTemplates e strictInjectionParameters tutti attivi, eppure 54 ': any' e 46 'as any' sopravvivono su 3884 file. Perché strict mode elimina solo gli errori accidentali, non le vie di fuga deliberate, e cosa fare davvero con gli any residui.

Risposta diretta

In questo monorepo, tsconfig ha strict, strictTemplates e strictInjectionParameters tutti a true, eppure sopravvivono 54 usi espliciti di ': any' e 46 cast 'as any' su 3884 file TypeScript. Strict mode elimina gli errori di tipo accidentali, non quelli deliberati: any resta una via di fuga disponibile a chi ha fretta, e nessuna opzione del compilatore puo chiuderla del tutto.

"Ho strict: true, quindi il mio codebase è type-safe" è una frase che sento spesso e che sento raramente verificata su un progetto reale, di dimensioni reali. Strict mode è necessario. Non è sufficiente. La differenza tra i due aggettivi vale 100 breach espliciti di tipo su questo stesso portfolio, tutti passati la review, tutti compilati senza un solo errore.

Disclaimer di metodo: i numeri vengono da un grep diretto sul sorgente (grep -ro ": any\b" src/app e grep -ro "as any\b" src/app), non da un'analisi teorica. Sono verificabili da chiunque abbia accesso al repository.

Cosa fa davvero strict mode (e cosa non fa)

Il flag strict: true di TypeScript è in realtà un pacchetto di otto flag distinti (strictNullChecks, noImplicitAny, strictFunctionTypes e altri). Questo progetto li ha tutti attivi, più due estensioni specifiche di Angular: strictTemplates (type-checking nei template HTML) e strictInjectionParameters (dependency injection tipizzata).

Quello che strict mode elimina davvero sono gli errori accidentali: una variabile che il compilatore avrebbe inferito come any senza noImplicitAny, un accesso a una proprietà potenzialmente null senza strictNullChecks. Sono errori che nessuno scrive di proposito: emergono dall'assenza di vincoli, non da una scelta esplicita dello sviluppatore.

Quello che strict mode NON elimina è la via di fuga deliberata: any scritto a mano, o un cast as SomeType che dice al compilatore "fidati di me" senza che sia vero. Nessun flag del compilatore può impedire a uno sviluppatore sotto pressione di scrivere const data: any = response.body per far compilare il codice e passare oltre.

Il dato: 100 breach espliciti su 3884 file

Il conteggio esatto, a oggi:

  • 54 occorrenze di : any (tipizzazione esplicita ad any).
  • 46 occorrenze di as any (cast forzato).
  • 37 funzioni generiche (function nome<T>) come confronto: la parte "avanzata" di TypeScript che questo articolo non copre, ma che coesiste con gli stessi 100 breach.
  • 3884 file TypeScript totali in src/app.

100 breach su 3884 file è meno del 3% dei file, e molte occorrenze condividono lo stesso file. Non è un allarme rosso. È la prova empirica che anche in un codebase disciplinato, con ogni flag di rigidità disponibile attivo, any non scompare mai del tutto: si riduce a un residuo misurabile, non a zero.

Dove finiscono davvero gli any: i confini del sistema

Non tutti gli any sono uguali. Guardando dove si concentrano nel codice, i pattern ricorrenti sono tre:

  1. Boundary con librerie esterne non tipizzate o parzialmente tipizzate. Una libreria di terze parti senza @types aggiornati, o con tipi generici troppo larghi per essere utili, spinge verso as any come via più rapida rispetto a scrivere una dichiarazione di tipo custom per codice che non controlli.
  2. API browser/SSR ambigue. Codice che deve funzionare sia server-side (Node/Express) sia client-side (browser), dove un oggetto globale ha forma diversa nei due ambienti, a volte finisce per usare any invece di un union type esplicito, soprattutto quando il controllo isPlatformBrowser già gestisce la logica e il tipo sembra ridondante.
  3. Deadline sotto pressione. Il caso più onesto e più comune: un componente che deve andare in produzione, un tipo complesso da derivare correttamente (generics annidati, mapped types), e la scelta pragmatica di sbloccare il commit con any invece di passare venti minuti a scrivere il tipo corretto.

Il trade-off: unknown, type guard, o any onesto

Qui arriva la parte controversa dell'articolo. La risposta da manuale è sempre "usa unknown invece di any, poi restringi con un type guard". È corretta in teoria. Nella pratica di un boundary con una libreria esterna, unknown sposta il costo dello sblocco dal punto di ingresso a ogni singolo punto di uso a valle, perché ogni accesso a una proprietà richiede prima un narrowing esplicito.

A volte quel costo vale la pena (dati che attraversano un confine di sicurezza, input utente, risposte API non fidate). Altre volte no: un any dichiarato con un commento che spiega perché (// any: libreria X non ha types, verificato manualmente v2.1) è più onesto di un as SomeType che finge una garanzia di tipo che il compilatore non sta realmente verificando. Un cast bugiardo è peggio di un any dichiarato, perché il primo dà una falsa sensazione di sicurezza al prossimo sviluppatore che legge quel codice.

Eliminare gli ultimi any residui ha un rendimento marginale decrescente: i primi cento probabilmente valgono lo sforzo, gli ultimi dieci, spesso legati a boundary esterni genuinamente non tipizzabili, no.

Cosa farei diversamente

Non cambierei la scelta di strict mode al 100%: resta la base corretta. Quello che introdurrei è un lint rule dedicato (@typescript-eslint/no-explicit-any in modalità "warn", non "error") con un commento obbligatorio su ogni occorrenza che spiega il motivo del boundary, invece di lasciare che any passi silenzioso in code review solo perché compila. Il problema non è che esistano 100 any: è che nessuno dei 100 dichiara perché è lì.


Domande frequenti

Cosa attiva davvero il flag strict di TypeScript?

strict: true attiva un pacchetto di otto flag distinti, tra cui strictNullChecks, noImplicitAny e strictFunctionTypes, che insieme eliminano gli errori di tipo accidentali (variabili inferite come any, accessi a proprietà potenzialmente null) ma non impediscono l'uso deliberato di any scritto esplicitamente dallo sviluppatore.

Perché un progetto con strict mode al 100% ha ancora usi di any?

Perché any resta una via di fuga sempre disponibile al compilatore: nessun flag di rigidità può impedire a uno sviluppatore di scrivere esplicitamente any per far compilare del codice velocemente, specialmente su boundary con librerie esterne non tipizzate o sotto pressione di deadline.

Meglio usare unknown o any quando un tipo non è chiaro?

Dipende dal contesto: unknown è più sicuro perché richiede un narrowing esplicito prima di ogni uso, ma sposta il costo dal punto di ingresso a ogni punto di uso a valle. Su dati che attraversano un confine di sicurezza vale la pena, su boundary genuinamente non tipizzabili un any dichiarato con un commento esplicativo è spesso più onesto.

Perché un cast 'as SomeType' può essere peggio di un any dichiarato?

Perché un cast forzato finge una garanzia di tipo che il compilatore non sta realmente verificando, dando una falsa sensazione di sicurezza al prossimo sviluppatore che legge quel codice, mentre un any dichiarato esplicitamente è almeno onesto sul fatto che quel valore non è verificato.

Vale la pena eliminare tutti gli any residui in un codebase strict?

No, non tutti: eliminare gli any ha un rendimento marginale decrescente. I primi sono spesso facili da correggere, ma gli ultimi residui tendono a essere legati a boundary esterni genuinamente non tipizzabili, dove il costo di eliminarli supera il beneficio reale in sicurezza di tipo.

Fonti e approfondimenti

Prova Questi Strumenti

Strumenti gratuiti correlati a questo argomento.

Commenti

Caricamento commenti...

Accedi per commentare

Discuti questo articolo nella community

Unisciti al gruppo Telegram per discutere con altri sviluppatori, fare domande e condividere le tue esperienze.

Ti è piaciuto questo articolo?

Esplora altri contenuti sul blog o scopri i miei progetti