<h2>TypeScript Compiler API: tsconfig Avançado e Project References</h2>
<h3>Entendendo o tsconfig.json Avançado</h3>
<p>O arquivo <code>tsconfig.json</code> é muito mais que um simples arquivo de configuração. Ele controla como o compilador TypeScript processa seu código, otimiza builds e gerencia dependências entre projetos. Quando trabalhamos com aplicações grandes, dominar as opções avançadas é essencial para manter a performance e a qualidade do código.</p>
<p>As opções mais críticas para projetos profissionais incluem <code>incremental</code>, <code>composite</code>, <code>declaration</code> e <code>moduleResolution</code>. A opção <code>incremental</code> permite que o TypeScript armazene em cache informações de compilação anteriores, acelerando recompilações. Já <code>composite</code> prepara seu projeto para ser referenciado por outros, essencial quando usamos Project References.</p>
<pre><code class="language-json">{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020"],
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"incremental": true,
"tsBuildInfoFile": "./.tsbuildinfo",
"composite": true,
"declaration": true
},
"include": ["src/*/"],
"exclude": ["node_modules", "dist", "*/.spec.ts"]
}</code></pre>
<h3>Project References: Dividindo Projetos Grandes</h3>
<p>Project References permitem que você organize código TypeScript em múltiplos projetos compilados independentemente. Isso é revolucionário para monorepos e aplicações complexas onde diferentes partes do código têm ciclos de compilação diferentes.</p>
<p>Quando você ativa <code>composite: true</code> e <code>declaration: true</code> em um <code>tsconfig.json</code>, esse projeto pode ser referenciado por outros. A chave está em usar <code>references</code> no arquivo raiz para informar ao compilador as dependências entre projetos.</p>
<pre><code class="language-json">{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"composite": true,
"declaration": true,
"declarationMap": true,
"incremental": true,
"tsBuildInfoFile": "./.tsbuildinfo"
},
"include": ["src/*/"],
"references": [
{ "path": "../common" },
{ "path": "../api" }
]
}</code></pre>
<p>A estrutura de um monorepo típico seria:</p>
<pre><code>monorepo/
├── tsconfig.json (raiz)
├── common/
│ ├── tsconfig.json (composite: true)
│ └── src/
│ ├── types.ts
│ └── utils.ts
├── api/
│ ├── tsconfig.json (composite: true, references: [common])
│ └── src/
│ └── server.ts
└── web/
├── tsconfig.json (composite: true, references: [common, api])
└── src/
└── app.tsx</code></pre>
<h3>Compilação Incremental e Build Mode</h3>
<p>A compilação incremental é um game-changer para projetos grandes. O TypeScript armazena informações do build anterior em <code>tsBuildInfoFile</code>, permitindo que recompilações processem apenas arquivos que mudaram. Combinado com Project References, você obtém builds extremamente rápidos.</p>
<pre><code class="language-typescript">// scripts/build.ts - usando a TypeScript Compiler API
import * as ts from 'typescript';
import * as path from 'path';
function buildProject(projectPath: string) {
const configPath = ts.findConfigFile(
projectPath,
ts.sys.fileExists,
'tsconfig.json'
);
if (!configPath) {
throw new Error(tsconfig.json não encontrado em ${projectPath});
}
const config = ts.readConfigFile(configPath, ts.sys.readFile);
const parsedConfig = ts.parseJsonConfigFileContent(
config.config,
ts.sys,
path.dirname(configPath)
);
// Usar o builder para compilação incremental
const host = ts.createIncrementalCompilerHost(parsedConfig.options);
const builder = ts.createIncrementalProgram({
rootNames: parsedConfig.fileNames,
options: parsedConfig.options,
host
});
const result = builder.emit();
const diagnostics = ts.getPreEmitDiagnostics(builder.getProgram());
diagnostics.forEach(diagnostic => {
if (diagnostic.file) {
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(
diagnostic.start!
);
console.log(
${diagnostic.file.fileName} (${line + 1},${character + 1}): ${ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n')}
);
}
});
return { exitCode: result.emitSkipped ? 1 : 0, builder };
}
buildProject('./packages/common');</code></pre>
<h3>Estratégias Avançadas: PathMapping e Resoluções Customizadas</h3>
<p>Para projetos complexos, <code>baseUrl</code> e <code>paths</code> do <code>tsconfig.json</code> são essenciais. Eles permitem imports limpos sem caminhos relativos confusos. Combinados com Project References, criam uma estrutura de código profissional e escalável.</p>
<pre><code class="language-json">{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@common/": ["packages/common/src/"],
"@api/": ["packages/api/src/"],
"@types/": ["packages/common/src/types/"],
"@utils/": ["packages/common/src/utils/"]
},
"composite": true,
"declaration": true,
"incremental": true
},
"references": [
{ "path": "./packages/common" },
{ "path": "./packages/api" }
]
}</code></pre>
<p>Com essa configuração, você importa assim:</p>
<pre><code class="language-typescript">// Em packages/api/src/server.ts
import { User } from '@types/models';
import { formatDate } from '@utils/date';
import { UserService } from '@api/services/user';
export class ApiServer {
private userService: UserService;
constructor() {
this.userService = new UserService();
}
async getUser(id: string): Promise<User> {
const user = await this.userService.findById(id);
return {
...user,
createdAt: formatDate(user.createdAt)
};
}
}</code></pre>
<h2>Conclusão</h2>
<p>Dominar <code>tsconfig.json</code> avançado e Project References transforma sua capacidade de trabalhar com código TypeScript em larga escala. Os três pontos principais aprendidos são: <strong>(1)</strong> configurações como <code>incremental</code>, <code>composite</code> e <code>declaration</code> são fundamentais para builds rápidos e modulares; <strong>(2)</strong> Project References permitem que você organize código em múltiplos projetos compilados independentemente, acelerando o desenvolvimento; <strong>(3)</strong> <code>baseUrl</code> e <code>paths</code>, quando bem planejados, criam uma estrutura de imports intuitiva e escalável que melhora significativamente a manutenibilidade do projeto.</p>
<p>Aplique esses conceitos progressivamente: comece com configurações básicas, evoluia para Project References quando seu projeto crescer, e domine path mapping quando sua estrutura exigir múltiplos pacotes interdependentes.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://www.typescriptlang.org/tsconfig" target="_blank" rel="noopener noreferrer">TypeScript Handbook - tsconfig.json Reference</a></li>
<li><a href="https://www.typescriptlang.org/docs/handbook/project-references.html" target="_blank" rel="noopener noreferrer">TypeScript Project References Documentation</a></li>
<li><a href="https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API" target="_blank" rel="noopener noreferrer">TypeScript Compiler API Documentation</a></li>
<li><a href="https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html" target="_blank" rel="noopener noreferrer">Building Large-Scale TypeScript Applications - Microsoft</a></li>
<li><a href="https://www.typescriptlang.org/docs/handbook/project-references.html#handbook-content" target="_blank" rel="noopener noreferrer">Monorepos with TypeScript and Project References</a></li>
</ul>