{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src",
"composite": true,
"declaration": true,
"declarationMap": true
},
"include": ["src/**/*"],
"references": [
{ "path": "../shared-utils" }
]
}Am lucrat anul trecut pe un monorepo cu 14 pachete interne unde tsc dura aproape 4 minute la fiecare build pe CI. Tentația inițială a echipei a fost să arunce mai mult RAM în runner-ul de GitHub Actions, dar problema era complet alta. Oamenii configuraseră doar opțiunea paths în tsconfig.json și ignoraseră complet arhitectura de compilare din TypeScript.
Când ai mai multe pachete dependente între ele, modul în care configurezi tsconfig.json face diferența între o dezvoltare fluidă și momente în care îți vine să schimbi meseria.
Capcana numită 'paths'
Cea mai frecventă greșeală pe care o văd este utilizarea alias-urilor de tip paths ca soluție universală pentru monorepo. Pui "@corp/ui": ["packages/ui/src"] în fișierul rădăcină și gata, importurile arată curat.
Problemă: paths este doar un truc de rezolvare a modulului pentru compilator. TS tratează codul din pachetul importat ca și cum ar face parte din proiectul curent. Dacă ai un pachet core importat în alte 5 aplicații, TS va reanaliza și recompila codul din core de 5 ori. La un proiect cu peste 8k de useri și zeci de mii de linii de cod, chestia asta îți omoară complet timpii de build.
Soluția reală: Composite și Project References
Pentru a seta o graniță clară între pachete, trebuie să activezi flag-ul composite: true în fiecare pachet dependent și să folosești references în proiectul principal.
Când activezi composite, impui niște reguli stricte:
- Opțiunea
declarationdevine obligatorie (TS trebuie să genereze fișiere.d.ts). - Toate fișierele din pachet trebuie incluse explicit sau prin tipare.
- Se activează automat
incremental: true, generând un fișier.tsbuildinfo.
Magia are loc când rulezi tsc -b (sau tsc --build). În loc să verifice fiecare fișier .ts la fiecare pas, compilatorul se uită în .tsbuildinfo și în fișierele .d.ts deja generate. Dacă packages/ui nu s-a schimbat, TS nici măcar nu-l atinge. Îl sare complet.
Trade-off-uri reale: DX vs Performanță
Toată viteza asta vine cu un preț. Nu există magie fără un compromis în DX:
- Build pipeline obligatoriu: Dacă schimbi un tip în
packages/common, trebuie să rulezi build pecommonînainte ca aplicația ta Web să vadă noua definiție. Dacă nu folosești un tool ca Turborepo sau Nx să gestioneze asta în fundal, devine frustrant. - Fișiere
.d.tspeste tot: Proiectul devine puțin mai 'poluat' în folderul de output. Trebuie să ai grijă să pui.tsbuildinfoîn.gitignoredacă nu vrei commit-uri inutile de 10k linii. - Configurație mai verboasă: Nu mai ai un singur
tsconfig.jsonatotputernic. Ai nevoie de o strategie de moștenire (untsconfig.base.jsondin care extinzi).
La noi, trecerea la project references + Turborepo a scăzut timpul de build de la 3m 50s la doar 45 de secunde pe un CI rece, iar la recompilări incrementale durează sub 5 secunde. Se simte masiv.
Voi cum gestionați structura de TS în monorepo? Mergeți pe tsc -b nativ sau lăsați un bundler (Vite/Esbuild) să ignore type-checking-ul în dev și faceți verificarea globală doar la commit?