TypeScript

tsconfig.json Avançado: Todas as Opções que Importam na Prática: Do Básico ao Avançado

17 min de leitura

tsconfig.json Avançado: Todas as Opções que Importam na Prática: Do Básico ao Avançado

Entendendo o tsconfig.json: Fundamentos Práticos O é o arquivo de configuração central do TypeScript. Ele define como o compilador deve se comportar, quais arquivos processar, para qual versão JavaScript compilar e como lidar com módulos. Muitos desenvolvedores o deixam com configurações padrão, perdendo controle fino sobre seu projeto. Dominar suas opções significa ter precisão na compilação, melhor compatibilidade e menos bugs em tempo de execução. Quando você executa sem argumentos, o TypeScript procura por no diretório atual ou em diretórios pai. Se encontrado, usa suas configurações. Se não encontrado, usa padrões muito permissivos que aceitam quase qualquer código. Um bom é como um contrato bem definido entre você e o compilador: você especifica regras estritas, o compilador as aplica rigorosamente. Opções de Compilação Essenciais Target e Lib: Definindo o Destino A opção define para qual versão do ECMAScript o TypeScript deve compilar. Se seu projeto precisa rodar em navegadores antigos, use . Se é um projeto moderno, ou superior faz

<h2>Entendendo o tsconfig.json: Fundamentos Práticos</h2>

<p>O <code>tsconfig.json</code> é o arquivo de configuração central do TypeScript. Ele define como o compilador deve se comportar, quais arquivos processar, para qual versão JavaScript compilar e como lidar com módulos. Muitos desenvolvedores o deixam com configurações padrão, perdendo controle fino sobre seu projeto. Dominar suas opções significa ter precisão na compilação, melhor compatibilidade e menos bugs em tempo de execução.</p>

<p>Quando você executa <code>tsc</code> sem argumentos, o TypeScript procura por <code>tsconfig.json</code> no diretório atual ou em diretórios pai. Se encontrado, usa suas configurações. Se não encontrado, usa padrões muito permissivos que aceitam quase qualquer código. Um bom <code>tsconfig.json</code> é como um contrato bem definido entre você e o compilador: você especifica regras estritas, o compilador as aplica rigorosamente.</p>

<h2>Opções de Compilação Essenciais</h2>

<h3>Target e Lib: Definindo o Destino</h3>

<p>A opção <code>target</code> define para qual versão do ECMAScript o TypeScript deve compilar. Se seu projeto precisa rodar em navegadores antigos, use <code>ES5</code>. Se é um projeto moderno, <code>ES2020</code> ou superior faz sentido. A opção <code>lib</code> especifica quais bibliotecas de tipo estão disponíveis (DOM, ES6, etc.). Muitas vezes, você precisa ambas bem alinhadas.</p>

<pre><code class="language-json">{

&quot;compilerOptions&quot;: {

&quot;target&quot;: &quot;ES2020&quot;,

&quot;lib&quot;: [&quot;ES2020&quot;, &quot;DOM&quot;, &quot;DOM.Iterable&quot;],

&quot;module&quot;: &quot;ESNext&quot;

}

}</code></pre>

<p>No exemplo acima, estamos dizendo: compile para JavaScript moderno (ES2020), assume que teremos acesso às APIs do DOM e dos iteráveis, e use módulos ESNext (o compilador preservará a sintaxe <code>import/export</code> para um bundler lidar depois). Se seu projeto roda em Node.js puro, não inclua <code>DOM</code> na lib. Se precisa de funcionalidades específicas como <code>Promise</code> ou <code>Proxy</code>, garanta que estejam na lib.</p>

<h3>Module: Qual Sistema de Módulos Usar</h3>

<p>Existem vários sistemas de módulos: CommonJS (padrão Node.js antigo), ESNext (módulos modernos), UMD (compatível com múltiplos ambientes) e outros. A escolha depende do seu bundler e ambiente de execução.</p>

<pre><code class="language-json">{

&quot;compilerOptions&quot;: {

&quot;module&quot;: &quot;commonjs&quot;,

&quot;target&quot;: &quot;ES2020&quot;

}

}</code></pre>

<p>Se usar Webpack, Vite ou Rollup, configure <code>module: &quot;ESNext&quot;</code> — esses bundlers entendem módulos ES6 nativamente e fazem otimizações melhores. Se seu código roda direto no Node.js sem bundler, use <code>commonjs</code>. Para bibliotecas NPM publicadas, muitas vezes você precisa gerar ambos: uma versão CommonJS (em <code>dist/cjs</code>) e uma ESNext (em <code>dist/esm</code>).</p>

<h3>Strict: Ativando Segurança Total</h3>

<p>A opção <code>strict</code> ativa um conjunto de verificações rigorosas simultaneamente. É como ligar todas as luzes de segurança de uma só vez.</p>

<pre><code class="language-json">{

&quot;compilerOptions&quot;: {

&quot;strict&quot;: true,

&quot;noImplicitAny&quot;: true,

&quot;strictNullChecks&quot;: true,

&quot;strictFunctionTypes&quot;: true,

&quot;strictBindCallApply&quot;: true,

&quot;strictPropertyInitialization&quot;: true,

&quot;noImplicitThis&quot;: true,

&quot;alwaysStrict&quot;: true

}

}</code></pre>

<p>Ao ativar <code>strict: true</code>, o TypeScript força você a ser explícito sobre tipos. Variáveis não podem ser <code>any</code> implícito, valores não podem ser <code>null</code> ou <code>undefined</code> sem declaração, e métodos precisam ser tipados corretamente. Isso evita toneladas de bugs. Se estiver herdando um projeto legado sem tipos, comece com <code>strict: false</code> e migre gradualmente para <code>true</code>.</p>

<h3>Declaration e DeclarationMap: Gerando Tipos para Consumidores</h3>

<p>Se você está criando uma biblioteca NPM, precisa fornecer informações de tipo para consumidores. A opção <code>declaration</code> gera arquivos <code>.d.ts</code> (arquivos de declaração). A opção <code>declarationMap</code> cria source maps para esses arquivos, permitindo navegar até o código original em IDEs.</p>

<pre><code class="language-json">{

&quot;compilerOptions&quot;: {

&quot;declaration&quot;: true,

&quot;declarationMap&quot;: true,

&quot;outDir&quot;: &quot;./dist&quot;,

&quot;rootDir&quot;: &quot;./src&quot;

}

}</code></pre>

<p>Quando você compila com essas opções, TypeScript gera um <code>.d.ts</code> para cada <code>.ts</code>. Uma ferramenta como <code>dts-bundle-generator</code> depois consolida tudo em um único arquivo de tipo. Consumidores da sua biblioteca obterão IntelliSense perfeito. Sem <code>declaration</code>, sua biblioteca é praticamente invisível para usuários TypeScript.</p>

<h2>Paths, Resolução de Módulos e BaseUrl</h2>

<h3>Configurando Aliases com Paths</h3>

<p>Em projetos grandes, caminhos relativos como <code>../../../../utils/helper</code> são ruins: difíceis de ler e quebram quando você move arquivos. A solução é usar aliases.</p>

<pre><code class="language-json">{

&quot;compilerOptions&quot;: {

&quot;baseUrl&quot;: &quot;.&quot;,

&quot;paths&quot;: {

&quot;@/&quot;: [&quot;src/&quot;],

&quot;@components/&quot;: [&quot;src/components/&quot;],

&quot;@utils/&quot;: [&quot;src/utils/&quot;],

&quot;@types/&quot;: [&quot;src/types/&quot;]

}

}

}</code></pre>

<p>Agora, em qualquer arquivo, você pode fazer:</p>

<pre><code class="language-typescript">import { Button } from &quot;@components/button&quot;;

import { formatDate } from &quot;@utils/date&quot;;

import type { User } from &quot;@types/user&quot;;</code></pre>

<p>Muito melhor. Seu bundler (Webpack, Vite, etc.) entende essas configurações automaticamente. Se estiver usando Jest para testes, configure <code>moduleNameMapper</code> no jest.config.js para que funcione lá também. TypeScript faz parte da história, mas não é toda.</p>

<h3>Module Resolution: Node vs Classic</h3>

<p>TypeScript suporta dois algoritmos de resolução de módulos. <code>node</code> segue a estratégia do Node.js (procura em <code>node_modules</code>, depois em diretórios pai). <code>classic</code> é o padrão antigo do TypeScript (quase obsoleto). Use sempre <code>node</code>.</p>

<pre><code class="language-json">{

&quot;compilerOptions&quot;: {

&quot;moduleResolution&quot;: &quot;node&quot;,

&quot;resolveJsonModule&quot;: true,

&quot;esModuleInterop&quot;: true,

&quot;allowSyntheticDefaultImports&quot;: true

}

}</code></pre>

<p>A opção <code>resolveJsonModule</code> permite importar arquivos JSON diretamente (útil para configs). <code>esModuleInterop</code> corrige problemas de compatibilidade entre CommonJS e ESM. <code>allowSyntheticDefaultImports</code> permite sintaxe mais limpa ao importar CommonJS de código ESM.</p>

<h2>Controle de Compilação e Diretórios</h2>

<h3>Include, Exclude e Files</h3>

<p>TypeScript precisa saber quais arquivos compilar. Você controla isso com <code>include</code>, <code>exclude</code> e <code>files</code>. Use <code>include</code> para padrões (globs) e <code>exclude</code> para excluir caminhos específicos.</p>

<pre><code class="language-json">{

&quot;include&quot;: [&quot;src/*/&quot;],

&quot;exclude&quot;: [&quot;node_modules&quot;, &quot;dist&quot;, &quot;*/.spec.ts&quot;, &quot;*/.test.ts&quot;],

&quot;compilerOptions&quot;: {

&quot;outDir&quot;: &quot;./dist&quot;,

&quot;rootDir&quot;: &quot;./src&quot;

}

}</code></pre>

<p>Este setup compila tudo em <code>src</code>, mas ignora testes (<code>.spec.ts</code> e <code>.test.ts</code>) e a pasta <code>node_modules</code>. Arquivos compilados vão para <code>dist</code>. Se usar <code>files</code> em vez de <code>include</code>, você lista arquivo por arquivo explicitamente — bom apenas para projetos muito pequenos.</p>

<h3>OutDir, RootDir e Preservando Estrutura</h3>

<p><code>rootDir</code> é o diretório raiz da sua fonte, <code>outDir</code> é onde TypeScript coloca os arquivos compilados. TypeScript preserva a estrutura de pastas, removendo <code>rootDir</code> do caminho.</p>

<pre><code class="language-json">{

&quot;compilerOptions&quot;: {

&quot;rootDir&quot;: &quot;./src&quot;,

&quot;outDir&quot;: &quot;./dist&quot;,

&quot;sourceMap&quot;: true,

&quot;inlineSourceMap&quot;: false

}

}</code></pre>

<p>Se você tiver <code>src/components/Button.ts</code>, ele será compilado para <code>dist/components/Button.js</code>. A opção <code>sourceMap</code> gera arquivos <code>.map</code> que mapeiam o código compilado de volta ao TypeScript original — essencial para debugar em produção. <code>inlineSourceMap</code> embuça o source map no próprio JavaScript (útil para ambientes onde o arquivo <code>.map</code> não está disponível, mas aumenta o tamanho do arquivo).</p>

<h3>Skiplib e SkipDefaults</h3>

<p><code>skipLibCheck</code> faz TypeScript não verificar tipos de bibliotecas da <code>node_modules</code>. Economiza tempo de compilação massivamente (até 50% em alguns projetos). Se uma biblioteca tem tipos ruins, você não quer descobrir durante sua compilação.</p>

<pre><code class="language-json">{

&quot;compilerOptions&quot;: {

&quot;skipLibCheck&quot;: true,

&quot;forceConsistentCasingInFileNames&quot;: true,

&quot;esModuleInterop&quot;: true

}

}</code></pre>

<p>A opção <code>forceConsistentCasingInFileNames</code> evita problemas em sistemas de arquivos case-sensitive (Linux/Mac) onde <code>Button.ts</code> e <code>button.ts</code> são arquivos diferentes — comum quando desenvolve em Windows e deploy em Linux.</p>

<h2>Segurança de Tipos Avançada</h2>

<h3>NoUnusedLocals e NoUnusedParameters</h3>

<p>Essas opções forçam você a remover código morto, mantendo o projeto limpo e focado.</p>

<pre><code class="language-typescript">function calculateTotal(items: Item[], tax: number, discount: number) {

// ❌ Com noUnusedParameters: true, discount causaria erro

return items.reduce((sum, item) =&gt; sum + item.price, 0) * (1 + tax);

}</code></pre>

<p>Com essas opções ativadas, qualquer variável ou parâmetro não usado gera um erro. Você é forçado a removê-lo ou usar. Parece chato, mas mantém seu código refatorado constantemente.</p>

<pre><code class="language-json">{

&quot;compilerOptions&quot;: {

&quot;noUnusedLocals&quot;: true,

&quot;noUnusedParameters&quot;: true,

&quot;noImplicitReturns&quot;: true,

&quot;noFallthroughCasesInSwitch&quot;: true

}

}</code></pre>

<p><code>noImplicitReturns</code> garante que todas as branches de uma função retornem algo. <code>noFallthroughCasesInSwitch</code> impede falls-through acidentais em switches sem <code>break</code>.</p>

<h3>Experimentais: useDefineForClassFields</h3>

<p>Quando seu código compila classes, TypeScript tem duas estratégias: uma segue o padrão JavaScript moderno (usando getters/setters definidos com <code>Object.defineProperty</code>), outra mais simples. A opção <code>useDefineForClassFields</code> usa a estratégia moderna.</p>

<pre><code class="language-typescript">class User {

name: string = &quot;Unknown&quot;; // Com useDefineForClassFields: true, comportamento diferente

constructor(name: string) {

this.name = name;

}

}</code></pre>

<p>Ative isso se seu <code>target</code> é <code>ES2022</code> ou superior. Evita problemas com herança e property descriptors.</p>

<pre><code class="language-json">{

&quot;compilerOptions&quot;: {

&quot;target&quot;: &quot;ES2022&quot;,

&quot;useDefineForClassFields&quot;: true

}

}</code></pre>

<h2>Exemplo Prático Completo</h2>

<p>Aqui está um <code>tsconfig.json</code> realista e bem construído para um projeto full-stack moderno:</p>

<pre><code class="language-json">{

&quot;compilerOptions&quot;: {

&quot;target&quot;: &quot;ES2020&quot;,

&quot;module&quot;: &quot;ESNext&quot;,

&quot;lib&quot;: [&quot;ES2020&quot;, &quot;DOM&quot;, &quot;DOM.Iterable&quot;],

&quot;jsx&quot;: &quot;react-jsx&quot;,

&quot;declaration&quot;: true,

&quot;declarationMap&quot;: true,

&quot;sourceMap&quot;: true,

&quot;outDir&quot;: &quot;./dist&quot;,

&quot;rootDir&quot;: &quot;./src&quot;,

&quot;baseUrl&quot;: &quot;.&quot;,

&quot;paths&quot;: {

&quot;@/&quot;: [&quot;src/&quot;],

&quot;@components/&quot;: [&quot;src/components/&quot;],

&quot;@hooks/&quot;: [&quot;src/hooks/&quot;],

&quot;@utils/&quot;: [&quot;src/utils/&quot;],

&quot;@types/&quot;: [&quot;src/types/&quot;]

},

&quot;strict&quot;: true,

&quot;noUnusedLocals&quot;: true,

&quot;noUnusedParameters&quot;: true,

&quot;noImplicitReturns&quot;: true,

&quot;noFallthroughCasesInSwitch&quot;: true,

&quot;esModuleInterop&quot;: true,

&quot;allowSyntheticDefaultImports&quot;: true,

&quot;forceConsistentCasingInFileNames&quot;: true,

&quot;resolveJsonModule&quot;: true,

&quot;skipLibCheck&quot;: true,

&quot;moduleResolution&quot;: &quot;node&quot;,

&quot;useDefineForClassFields&quot;: true,

&quot;isolatedModules&quot;: true

},

&quot;include&quot;: [&quot;src/*/&quot;],

&quot;exclude&quot;: [&quot;node_modules&quot;, &quot;dist&quot;, &quot;*/.spec.ts&quot;, &quot;*/.test.ts&quot;],

&quot;ts-node&quot;: {

&quot;compilerOptions&quot;: {

&quot;module&quot;: &quot;commonjs&quot;

}

}

}</code></pre>

<p>Neste setup: você compila para ES2020 (moderno), usa paths para organizar imports, ativa verificações estritas completas, gera tipos para consumidores, ignora bibliotecas em type-checking rápido com <code>skipLibCheck</code>, e permite que <code>ts-node</code> rode testes em CommonJS (compatível com Jest). É profissional, robusto e escalável.</p>

<h2>Conclusão</h2>

<p>Três lições essenciais sobre <code>tsconfig.json</code>: <strong>Primeiro</strong>, opções como <code>strict</code>, <code>noUnusedLocals</code> e <code>noImplicitReturns</code> não são detalhes — elas definem a qualidade do seu código. Ativá-las cedo previne bugs massivos depois. <strong>Segundo</strong>, <code>baseUrl</code> e <code>paths</code> transformam a legibilidade de projetos grandes — investir tempo aqui vale a pena. <strong>Terceiro</strong>, entender <code>target</code>, <code>lib</code>, <code>module</code> e <code>moduleResolution</code> é crucial quando seu código precisa rodar em ambientes diferentes (navegador, Node.js, bundlers variados). Não deixe essas configurações ao acaso.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://www.typescriptlang.org/tsconfig" target="_blank" rel="noopener noreferrer">TypeScript Handbook - Compiler Options</a></li>

<li><a href="https://basarat.gitbook.io/typescript/project/compilation-context" target="_blank" rel="noopener noreferrer">TypeScript Deep Dive - tsconfig.json</a></li>

<li><a href="https://www.typescriptlang.org/docs/handbook/module-resolution.html" target="_blank" rel="noopener noreferrer">TypeScript Official Documentation - Module Resolution</a></li>

<li><a href="https://www.npmjs.com/package/typescript" target="_blank" rel="noopener noreferrer">Configuring TypeScript for Production</a></li>

<li><a href="https://www.typescriptlang.org/docs/handbook/2/basic-types.html" target="_blank" rel="noopener noreferrer">Advanced TypeScript - Strict Mode Best Practices</a></li>

</ul>

<p>&lt;!-- FIM --&gt;</p>

Comentários

Mais em TypeScript

Como Usar Metadata e Reflect-Metadata em TypeScript com Decorators em Produção
Como Usar Metadata e Reflect-Metadata em TypeScript com Decorators em Produção

Entendendo Metadata e Reflect-Metadata Metadata é simplesmente informação sob...

O que Todo Dev Deve Saber sobre Node.js com TypeScript: Configuração, tsx e ts-node na Prática
O que Todo Dev Deve Saber sobre Node.js com TypeScript: Configuração, tsx e ts-node na Prática

Node.js com TypeScript: Configuração, tsx e ts-node na Prática TypeScript é u...

Boas Práticas de React Query com TypeScript: Queries, Mutations e Tipos Inferidos para Times Ágeis
Boas Práticas de React Query com TypeScript: Queries, Mutations e Tipos Inferidos para Times Ágeis

Por que React Query Revolucionou o Gerenciamento de Estado Durante minha carr...