eduardweb.
TypeScriptAvansat#typescript#monorepo#tsconfig#build-performance

Cum configurezi corect tsconfig într-un monorepo fără să distrugi CI-ul

De Cristian Barbu, 12 sept. 2026 · 16 vizualizări · 3 like-uri

Postat 12 sept. 2026
json
{
  "compilerOptions": {
    "composite": true,
    "declaration": true,
    "declarationMap": true,
    "rootDir": "src",
    "outDir": "dist"
  },
  "include": ["src"],
  "references": [
    { "path": "../core" },
    { "path": "../utils" }
  ]
}

Dacă ai aruncat vreodată cinci pachete într-un monorepo și ai rezolvat importurile punând niște wildcard-uri în paths, probabil știi deja dezastrul care urmează. La început pare elegant, dar când proiectul crește, tsc ajunge să verifice aceleași fișiere de zece ori și memoria explodează.

Am pățit treaba asta anul trecut pe un monorepo cu 14 pachete interne. Pe CI, un simplu pas de tsc --noEmit ajunsese să dureze peste 11 minute, iar local editorul începea să consume 4GB de RAM doar ca să-mi ofere autocomplete.

Iluzia numită paths

Cea mai comună greșeală e să configurezi un singur tsconfig.json în rădăcină și să mapezi modulele locale așa:

"@app/*": ["packages/*/src"]

Merge perfect în VS Code. Problema e că TypeScript tratează tot repository-ul ca pe un singur proiect gigantic. Dacă pachetul A depinde de pachetul B, iar ambele folosesc un tip comun din pachetul C, compilatorul nu știe că C a fost deja verificat. Reia analiza AST-ului de la zero pentru fiecare fișier importat.

În plus, rupi izolarea architecturală. Te trezești că pachetul tău de backend importă din greșeală un utilitar din frontend doar pentru că calea a fost rezolvată fără eroare de TypeScript.

Trecerea la Project References și composite

Soluția oficială, dar adesea ocolită pentru că e mai migăloasă la setup, se bazează pe două opțiuni: composite: true și references.

Fiecare pachet din packages/ trebuie să aibă propriul tsconfig.json. Când activezi "composite": true, obligi practic TypeScript să respecte trei reguli:

  • Generează automat fișiere .d.ts (deci ai nevoie de declaration: true).
  • Salvează metadatele compilării într-un fișier .tsbuildinfo (deci activează incremental: true by default).
  • Refuză să compileze dacă un fișier din exterior este importat fără să fie declarat explicit în references.

La rădăcină nu mai ții logica de compilare, ci doar un tsconfig.json „orchestrator” gol, cu files: [] și o listă de referințe către toate sub-proiectele.

Build incremental: de la 11 minute la 2.5 minute

Când rulezi tsc --build (sau prescurtat tsc -b), TypeScript citește graful de dependențe format din references. Știe exact ordinea topologică în care trebuie compilate pachetele.

Partea bună? Dacă modifici un singur fișier din packages/ui, TypeScript verifică doar packages/ui și proiectele care depind direct de el. Pe pachetele de backend sau core neatinse nu le mai ia la mână, ci citește direct fișierele .tsbuildinfo și declarațiile .d.ts deja emise.

La noi, trecerea asta a redus timpul de build pe CI de la 11 minute la 2 minute și jumătate la rece. Iar la cald, când un dev face un commit mic pe o ramură, verificarea tipurilor durează sub 8 secunde.

Trade-off-ul sincer: mentenanța doare

Nu e totul lapte și miere. Principalul dezavantaj e birocrația.

Dacă pachetul A are nevoie de o funcție din pachetul B, nu e de ajuns să instalezi dependența în package.json. Trebuie să mergi manual în packages/a/tsconfig.json și să adaugi { "path": "../b" } în array-ul de references. Dacă uiți, primești o eroare urâtă la compilare: Referenced project may not disable emit sau File is not under rootDir.

Merge impecabil dacă folosești un tool de automatizare (de exemplu, un script intern sau un generator de monorepo gen Nx/Turborepo care să-ți sincronizeze referințele). Fără un script care să verifice consistența între package.json și tsconfig.json, colegii mai juniori din echipă se vor bloca des în erori ciudate de TypeScript.

Voi cum gestionați asta în monorepo-uri mari? Lăsați bundler-ul (Vite/esbuild) să rezolve totul prin aliasuri sau forțați verificarea strictă prin tsc --build?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.