Hai mai aperto un progetto TypeScript e trovato un tsconfig.json pieno di opzioni senza capire cosa fanno? Ti è capitato di passare ore a debuggare errori di tipo che potevi prevenire con la configurazione giusta? Non sei solo. Ogni settimana vediamo sviluppatori e team che perdono tempo su dettagli banali perché il file di configurazione era impostato male o, peggio, lasciato di default.
Noi, di Meteora Web, lavoriamo con TypeScript su progetti reali da anni — piattaforme Laravel/Vue, sistemi di e-commerce complessi, tool interni. Veniamo dalla contabilità e dalla gestione aziendale: ogni ora persa su un bug evitabile è un costo. Per questo tsconfig.json non è un dettaglio: è il fondamento del tuo progetto. Se lo imposti bene, risparmi tempo, errori e soldi. Se lo ignori, paghi in refactoring e notti in bianco.
In questa guida ti portiamo dentro ogni opzione che conta, con esempi reali e il perché dietro ogni scelta. Alla fine saprai esattamente come configurare un progetto TypeScript solido, scalabile e manutenibile.
Perché tsconfig.json è il primo file che devi capire
Il file tsconfig.json dice al compilatore TypeScript cosa compilare, come compilarlo e dove mettere il risultato. Non è un ornamento: è il contratto tra te, il codice e l'ambiente di esecuzione. Lavoriamo quotidianamente con clienti che hanno ereditato progetti con configurazioni minime o sbagliate — codice che in locale funziona ma in produzione esplode, errori di tipo nascosti, output in cartelle sbagliate. Tutto evitabile con una configurazione chiara. Un sito o un'applicazione si misurano in fatturato, non in complimenti: se il codice ha bug perché la configurazione era lasca, il costo è reale.
Le opzioni fondamentali: target, module, lib, outDir, rootDir
Queste sono le prime che devi impostare. Definiscono la base del tuo progetto.
target — Versione ECMAScript di output
Specifica la versione di JavaScript in cui compilare. Scegli la più alta supportata dal tuo ambiente di esecuzione. Per Node.js 18+ usa ES2022; per browser moderni ES2020 va bene. Non mettere ES5 se non devi supportare IE11 — sprechi prestazioni e generi codice verboso.
"target": "ES2020"module — Sistema di moduli
Decide come vengono generati i moduli. Per Node.js moderno: "Node16" o "NodeNext". Per browser: "ESNext" o "ES2020". Attenzione: se usi CommonJS con codice moderno, rischi incoerenze di import/export.
lib — Librerie incluse
TypeScript include automaticamente le dichiarazioni di tipo per l'ambiente target. Puoi specificare esplicitamente: ["ES2020", "DOM", "DOM.Iterable"] per un frontend. Per backend Node, evita DOM. Non mettere ESNext se non hai un transpiler a valle — meglio essere espliciti.
"lib": ["ES2020", "DOM", "DOM.Iterable"]outDir e rootDir
outDir è la cartella di output (es. ./dist). rootDir è la radice dei file sorgente (es. ./src). Se non li imposti, TypeScript deduce la struttura ma spesso fuori controllo. Noi impostiamo sempre entrambi per mantenere pulito l'albero del progetto.
"outDir": "./dist",
"rootDir": "./src"Strict type checking: il tuo migliore amico (e a volte rompiscatole)
La bandiera strict: true abilita una serie di controlli che rendono il codice molto più robusto. È il pane quotidiano dei progetti che seguiamo: riduce gli errori a runtime, specialmente su sistemi contabili e di e-commerce dove un tipo sbagliato può causare calcoli errati.
Cosa attiva strict?
noImplicitAny: non permette variabili senza tipo esplicito. Se non lo attivi, perdi metà dei benefici di TypeScript.strictNullChecks: obbliga a gestirenulleundefined. Quante ore hai passato suCannot read property of undefined? Questo le elimina.strictFunctionTypes: controllo più rigoroso sui parametri delle funzioni. Previene bug subdoli.strictBindCallApply: controlla i metodibind,call,apply.alwaysStrict: usa"use strict"in ogni file.
Esempio di errore comune senza strictNullChecks:
const user = getUser(); // potrebbe restituire null
const name = user.name; // errore se user è nullCon strict: true TypeScript ti obbliga a scrivere if (user) ... o usare l'operatore ?..
Consiglio pratico: inizia sempre con strict: true. Se hai un codice legacy, disattiva solo le singole opzioni che ti bloccano, ma non rinunciare mai a strictNullChecks e noImplicitAny. Abbiamo visto progetti che per pigrizia hanno lasciato strict: false e poi hanno speso il triplo del tempo in debugging.
Gestione dei path: elimina le importazioni relative mostruose
Ti è mai capitato di scrivere ../../../components/Button? È un incubo da mantenere. Con baseUrl e paths puoi creare alias puliti.
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"]
}Ora puoi importare con import { Button } from '@components/Button'. Funziona anche con Webpack, Vite o esbuild se configurati di conseguenza. Noi usiamo questo pattern in tutti i progetti Laravel + Vue: rende il codice leggibile e rifattorizzabile.
Include, exclude e files: chi compili?
Per default TypeScript compila tutti i file .ts e .tsx nella cartella del progetto. Specifica include per restringere:
"include": ["src//*"],
"exclude": ["node_modules", "dist", "/*.test.ts"]Usa exclude per escludere cartelle di test, build, vendor. files invece è per elenchi espliciti (raramente necessario). Attenzione: se non escludi node_modules, il compilatore cercherà di compilare i pacchetti — lento e inutile.
Opzioni avanzate che fanno la differenza
sourceMap e declaration
sourceMap: true genera mappe di debug per il browser (fondamentale in sviluppo). declaration: true produce i file .d.ts per librerie condivisibili. Noi attiviamo declaration solo per pacchetti che esportiamo (es. componenti condivisi tra progetti).
resolveJsonModule e esModuleInterop
resolveJsonModule permette di importare file JSON direttamente. esModuleInterop risolve le incompatibilità tra moduli CommonJS e ES — se usi librerie Node, attivalo sempre.
"resolveJsonModule": true,
"esModuleInterop": trueskipLibCheck e forceConsistentCasingInFileNames
skipLibCheck: true salta il type-checking sui file .d.ts delle librerie — accelera la compilazione. forceConsistentCasingInFileNames evita errori di case nei filesystem case-sensitive (Linux). Entrambi consigliati.
Esempi pratici di configurazioni complete
Backend Node.js moderno
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"lib": ["ES2022"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"sourceMap": true,
"declaration": false
},
"include": ["src//*"],
"exclude": ["node_modules", "dist", "/*.test.ts"]
}Frontend React/Vite
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"jsx": "react-jsx",
"esModuleInterop": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"sourceMap": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}Errori comuni e come evitarli
1. strict: true rompe codice legacy
Succede spesso quando si migra un progetto JavaScript a TypeScript. Soluzione: attiva strict ma aggiungi // @ts-ignore temporaneamente (meglio: risolvi i tipi uno a uno). Noi consigliamo di partire con strict: true su nuovi progetti; per legacy, attiviamo gradualmente le sotto-opzioni.
2. Module non allineato con l'ambiente
Se usi module: "CommonJS" ma l'ambiente supporta ESM, rischi di generare codice con require invece di import — confusione e potenziali errori a runtime.
3. Paths non funzionano a runtime
Gli alias in paths valgono solo per TypeScript. Per farli funzionare a runtime devi configurare anche il bundler (Webpack, Vite, esbuild) con gli stessi alias.
In sintesi — cosa fare adesso
- Inizia con
strict: truein ogni nuovo progetto. È l'investimento più piccolo con il ritorno più grande. - Definisci
targetemodulein base al contesto di esecuzione (Node, browser, libreria). Non usareES5se non necessario. - Imposta
outDirerootDirper mantenere una struttura pulita. - Usa
pathscon un alias@/*per evitare importazioni relative lunghe. - Abilita
sourceMapin sviluppo,skipLibChecksempre,declarationsolo per librerie condivise. - Leggi la documentazione ufficiale una volta al mese: le opzioni si evolvono.
Se stai partendo da zero o devi rivedere un progetto esistente, ricorda: possedere il proprio stack batte affittarlo. Una configurazione solida è tua, è gratis, e ti dà il controllo completo. Noi di Meteora Web lo facciamo ogni giorno per i nostri clienti — e per noi stessi. TypeScript è un alleato, ma solo se lo configuri con la stessa cura che metti nel codice.
Sponsored Protocol