Pular para o conteúdo
TypeScript

Como Configurar tsconfig no TypeScript

tsconfig essencial: strict, moduleResolution e paths sem atirar no escuro.

Por que isso é importante

Configurar tsconfig no TypeScript começa por strict e moduleResolution alinhados ao bundler. Path aliases sem baseUrl certo só geram import quebrado.

O Que É o tsconfig.json

O tsconfig.json é um arquivo JSON na raiz do seu projeto que diz pro compilador TypeScript como se comportar. Sem ele, o compilador usa configurações padrão que podem não servir pro seu caso. Com ele, você controla tudo: pra qual versão de JavaScript compilar, quais arquivos incluir, quão rigorosa deve ser a checagem de tipos.

Quando você roda tsc (o compilador TypeScript), ele procura um tsconfig.json no diretório atual e sobe até a raiz do disco. Se não achar, compila com defaults. Em projetos profissionais, o tsconfig é um dos primeiros arquivos que você cria.

O arquivo tem três seções principais: compilerOptions (como compilar), include (quais arquivos compilar) e exclude (quais ignorar). A maior parte do trabalho fica no compilerOptions, que tem mais de 100 opções. Mas calma: na prática, você usa umas 15 a 20.

Passo a Passo: Configurando do Zero

Vamos montar um tsconfig.json partindo do zero. Cada passo adiciona uma camada de configuração.

  1. Passo 1 - Gere o arquivo base: Rode npx tsc --init na raiz do projeto. Isso cria um tsconfig.json com todas as opções comentadas e as mais comuns habilitadas. É um ótimo ponto de partida.
  2. Passo 2 - Configure o target: O target define pra qual versão de JavaScript o TypeScript compila. Use ES2020 ou ES2022 pra projetos Node.js modernos. Pra projetos que precisam rodar no browser com suporte amplo, use ES2015 ou ES2017.
  3. Passo 3 - Defina o module: Use commonjs pra projetos Node.js tradicionais ou ESNext/NodeNext pra projetos com ES Modules. Pra projetos com bundler (Webpack, Vite), use ESNext.
  4. Passo 4 - Ative strict: Strict é um atalho que ativa várias opções de segurança de uma vez: strictNullChecks, noImplicitAny, strictFunctionTypes e mais. Ative sempre. Sempre.
  5. Passo 5 - Configure paths pra aliases: Em vez de imports como ../../../utils/helper, configure @/utils/helper com paths aliases. Deixa o código limpo e facilita mover arquivos sem quebrar imports.
  6. Passo 6 - Defina include e exclude: Include diz quais pastas o TypeScript deve compilar (geralmente ["src"]). Exclude remove pastas que não devem ser compiladas (node_modules, dist, coverage).

compilerOptions: As Opções que Importam

Vamos ver cada grupo de opções com exemplos. Não precisa decorar tudo: entenda o conceito e copie a configuração que serve pro seu projeto.

Target e Module: Saída do Compilador

{
  "compilerOptions": {
    // Pra qual versão de JS compilar
    "target": "ES2022",

    // Sistema de módulos
    "module": "NodeNext",

    // Como resolver imports de módulos
    "moduleResolution": "NodeNext",

    // Pasta de saída dos arquivos compilados
    "outDir": "./dist",

    // Pasta raiz dos fontes
    "rootDir": "./src",

    // APIs disponíveis (DOM, ES2020, etc.)
    "lib": ["ES2022"]
  }
}

// Target comuns:
// "ES2015" - suporte amplo (async/await nativo não incluso)
// "ES2017" - async/await nativo
// "ES2020" - optional chaining, nullish coalescing
// "ES2022" - top-level await, at(), cause em Error
// "ESNext" - sempre a versão mais recente

Strict: Segurança Máxima

{
  "compilerOptions": {
    // Liga tudo de uma vez (recomendado)
    "strict": true,

    // O que strict ativa internamente:
    // "strictNullChecks": true    - null/undefined explícitos
    // "noImplicitAny": true       - proíbe any implícito
    // "strictFunctionTypes": true - checagem estrita de funções
    // "strictBindCallApply": true - bind/call/apply tipados
    // "noImplicitThis": true      - this precisa ter tipo
    // "alwaysStrict": true        - "use strict" em todo arquivo
    // "strictPropertyInitialization": true - props de classe iniciadas

    // Extras que valem ativar junto:
    "noUncheckedIndexedAccess": true,  // array[i] pode ser undefined
    "noImplicitReturns": true,         // todas as branches retornam
    "noFallthroughCasesInSwitch": true  // switch sem break é erro
  }
}

Paths: Aliases de Import

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      // Alias simples pra src/
      "@/*": ["src/*"],

      // Aliases específicos por módulo
      "@/utils/*": ["src/utils/*"],
      "@/components/*": ["src/components/*"],
      "@/types/*": ["src/types/*"],
      "@/services/*": ["src/services/*"]
    }
  }
}

// Antes (sem paths):
import { formatDate } from "../../../utils/date";
import { Button } from "../../components/ui/Button";

// Depois (com paths):
import { formatDate } from "@/utils/date";
import { Button } from "@/components/ui/Button";

// ATENÇÃO: paths só funciona no TypeScript.
// Se usar Node.js puro, configure também tsconfig-paths.
// Se usar bundler (Webpack, Vite), configure o alias lá também.

esModuleInterop e Resolução de Módulos

{
  "compilerOptions": {
    // Importar módulos CommonJS como default import
    "esModuleInterop": true,

    // Checagem consistente de casing em imports
    "forceConsistentCasingInFileNames": true,

    // Permitir import de JSON
    "resolveJsonModule": true,

    // Isolar cada arquivo como módulo
    "isolatedModules": true,

    // Não emitir JS (quando outro tool compila, ex: Babel, SWC)
    "noEmit": true,

    // Gerar source maps pra debug
    "sourceMap": true,

    // Gerar arquivos .d.ts de declaração
    "declaration": true
  }
}

// Com esModuleInterop:
import express from "express";        // funciona
import * as express from "express";   // também funciona

// Sem esModuleInterop:
import express from "express";        // ERRO
import * as express from "express";   // única opção

Include, Exclude e Extends

{
  // Herdar configuração base
  "extends": "@tsconfig/node20/tsconfig.json",

  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "./src"
  },

  // Quais arquivos/pastas compilar
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx"
  ],

  // Quais arquivos/pastas ignorar
  "exclude": [
    "node_modules",
    "dist",
    "coverage",
    "**/*.test.ts",
    "**/*.spec.ts"
  ]
}

// Presets populares pra extends:
// @tsconfig/node20       - Node.js 20
// @tsconfig/node18       - Node.js 18
// @tsconfig/recommended  - base recomendada
// @tsconfig/strictest    - mais rigoroso possível

// Instalar:
// npm install --save-dev @tsconfig/node20

Configurações Prontas por Tipo de Projeto

Em vez de montar do zero toda vez, aqui vão configs prontas pra cada cenário. Copie, cole e ajuste conforme seu projeto.

tsconfig para API Node.js

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "lib": ["ES2022"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "declaration": true,
    "sourceMap": true,
    "skipLibCheck": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  },
  "include": ["src/**/*.ts"],
  "exclude": ["node_modules", "dist", "**/*.test.ts"]
}

tsconfig para React (Next.js / Vite)

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "jsx": "react-jsx",
    "strict": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "skipLibCheck": true,
    "allowJs": true,
    "incremental": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  },
  "include": ["src/**/*.ts", "src/**/*.tsx"],
  "exclude": ["node_modules"]
}

Perceba que no projeto React com bundler, o noEmit é true porque quem compila o código é o Webpack, Vite ou SWC. O TypeScript só checa tipos. No Node.js, o TypeScript compila pra JavaScript, então outDir e declaration fazem sentido.

Erros Comuns na Configuração do tsconfig

Armadilhas que pegam em todo projeto

Não ativar strict: configuração padrão sem strict deixa passar null, any implícito e vários bugs silenciosos. Sempre ative strict. Resolva os erros que aparecem em vez de desligar a proteção.

Confundir module com moduleResolution: module define o formato de saída (CommonJS, ESNext). moduleResolution define como o TypeScript encontra arquivos importados (node, bundler, NodeNext). Os dois precisam combinar.

Paths sem baseUrl: paths aliases não funcionam sem baseUrl definido. Sempre coloque "baseUrl": "." junto com paths. E lembre: o bundler ou runtime também precisa saber dos aliases.

Include muito amplo: incluir "/*.ts" pega scripts de build, testes e tudo mais. Seja específico: "src//*.ts". Use exclude pra remover o que não deve ser compilado.

Copiar tsconfig sem entender: cada projeto tem necessidades diferentes. Um tsconfig de projeto React não serve pra API Node.js. Entenda cada opção antes de copiar.

Checklist de Configuração do tsconfig

  • Target definido conforme o ambiente de execução (Node.js ou browser)
  • Module e moduleResolution compatíveis entre si
  • Strict ativado com todas as sub-opções
  • Paths aliases configurados com baseUrl
  • Include apontando apenas pra pasta de código-fonte
  • Exclude removendo node_modules, dist e testes
  • esModuleInterop ativado pra compatibilidade de imports
  • resolveJsonModule habilitado se importar arquivos JSON
  • sourceMap ativado pra facilitar debug
  • skipLibCheck ativado pra builds mais rápidos

Configure TypeScript com Confiança

O tsconfig.json é a base de todo projeto TypeScript profissional. No CrazyStack, cada projeto começa com uma configuração sólida: strict mode, paths aliases, presets otimizados. Você constrói um SaaS completo com Node.js e React, aprendendo não só a configurar mas a entender cada decisão por trás do setup.

Se você quer parar de copiar configurações e começar a entender o que cada opção faz, esse é o caminho.