Como Usar TypeScript Compiler API: tsconfig Avançado e Project References em Produção Já leu

TypeScript Compiler API: tsconfig Avançado e Project References Entendendo o tsconfig.json Avançado O arquivo é 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. As opções mais críticas para projetos profissionais incluem , , e . A opção permite que o TypeScript armazene em cache informações de compilação anteriores, acelerando recompilações. Já prepara seu projeto para ser referenciado por outros, essencial quando usamos Project References. Project References: Dividindo Projetos Grandes 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. Quando você ativa e em um , esse projeto pode ser referenciado por outros. A chave está em usar no arquivo raiz para informar

TypeScript Compiler API: tsconfig Avançado e Project References

Entendendo o tsconfig.json Avançado

O arquivo tsconfig.json é 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.

As opções mais críticas para projetos profissionais incluem incremental, composite, declaration e moduleResolution. A opção incremental permite que o TypeScript armazene em cache informações de compilação anteriores, acelerando recompilações. Já composite prepara seu projeto para ser referenciado por outros, essencial quando usamos Project References.

{
  "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"]
}

Project References: Dividindo Projetos Grandes

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.

Quando você ativa composite: true e declaration: true em um tsconfig.json, esse projeto pode ser referenciado por outros. A chave está em usar references no arquivo raiz para informar ao compilador as dependências entre projetos.

{
  "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" }
  ]
}

A estrutura de um monorepo típico seria:

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

Compilação Incremental e Build Mode

A compilação incremental é um game-changer para projetos grandes. O TypeScript armazena informações do build anterior em tsBuildInfoFile, permitindo que recompilações processem apenas arquivos que mudaram. Combinado com Project References, você obtém builds extremamente rápidos.

// 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');

Estratégias Avançadas: PathMapping e Resoluções Customizadas

Para projetos complexos, baseUrl e paths do tsconfig.json 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.

{
  "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" }
  ]
}

Com essa configuração, você importa assim:

// 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)
    };
  }
}

Conclusão

Dominar tsconfig.json 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: (1) configurações como incremental, composite e declaration são fundamentais para builds rápidos e modulares; (2) Project References permitem que você organize código em múltiplos projetos compilados independentemente, acelerando o desenvolvimento; (3) baseUrl e paths, quando bem planejados, criam uma estrutura de imports intuitiva e escalável que melhora significativamente a manutenibilidade do projeto.

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.

Referências


Artigos relacionados