tsconfig.json é o arquivo de configuração de um projeto TypeScript. Quando você roda tsc sem argumentos, o compilador o procura na pasta atual (e depois nas pastas acima), lê as opções em compilerOptions e verifica os arquivos listados em include. Um exemplo pequeno, mas completo:
{
"compilerOptions": {
"target": "es2022",
"module": "nodenext",
"strict": true,
"rootDir": "./src",
"outDir": "./dist"
},
"include": ["src"]
}
Isso compila todos os arquivos TypeScript de src para JavaScript em dist, como código ES2022, usando as regras de módulo do Node.js e com todas as verificações strict ligadas. npx tsc --init gera um arquivo inicial mais longo, com um comentário em cada opção.
O arquivo é JSON com comentários: comentários //, comentários /* */ e vírgulas no final são todos aceitos.
Uma configuração inicial recomendada
Para uma aplicação ou script Node.js:
{
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist",
"target": "es2024",
"lib": ["es2024"],
"module": "nodenext",
"types": ["node"],
"strict": true,
"noUncheckedIndexedAccess": true,
"verbatimModuleSyntax": true,
"skipLibCheck": true,
"sourceMap": true
},
"include": ["src"]
}
Ela precisa de npm install --save-dev @types/node para os tipos do Node.js. Com module: "nodenext", o campo "type" do package.json decide se um arquivo .ts é um módulo ES ("type": "module") ou CommonJS ("type": "commonjs", que o npm init -y grava). Use "type": "module" em projetos novos que usam import e export.
Para código que um bundler como Vite, esbuild ou webpack compila, o bundler gera o JavaScript e o tsc só verifica:
{
"compilerOptions": {
"target": "es2022",
"lib": ["es2022", "dom"],
"module": "preserve",
"noEmit": true,
"strict": true,
"noUncheckedIndexedAccess": true,
"verbatimModuleSyntax": true,
"isolatedModules": true,
"skipLibCheck": true,
"jsx": "react-jsx"
},
"include": ["src"]
}
Os starters de frameworks (Vite, Next.js, Angular) geram o próprio tsconfig.json. Parta do deles em vez de substituí-lo.
As opções que importam
| Opção | O que faz | Padrão no TypeScript 7 |
|---|---|---|
target | Versão de JavaScript da saída; sintaxe mais nova é reescrita para targets mais antigos | es2025 |
lib | Tipos das APIs nativas que o verificador conhece (Array.prototype.at, Map, document) | Acompanha o target, mais o DOM |
module | Tipo de código de módulo gerado e regras de módulo aplicadas | esnext |
moduleResolution | Como os caminhos de import são encontrados no disco | nodenext ou node16 com o module correspondente, senão bundler |
strict | Liga toda a família de verificações strict | true |
rootDir | Pasta cuja estrutura é espelhada em outDir | A pasta que contém o tsconfig.json |
outDir | Para onde vão os arquivos .js (e .d.ts) | Ao lado de cada arquivo-fonte |
include / exclude / files | Quais arquivos fazem parte do projeto | Todo arquivo .ts dentro da pasta |
types | Quais pacotes @types são carregados sem import | [], nenhum |
noEmit | Só verifica, não grava nada | false |
declaration | Também grava arquivos de declaração de tipos .d.ts | false |
sourceMap | Grava arquivos .js.map para depuradores | false |
esModuleInterop | Faz import x from "cjs-package" funcionar com pacotes CommonJS | true (não pode ser desligado) |
skipLibCheck | Pula a verificação de tipos dos arquivos .d.ts, incluindo os de node_modules | false |
target e lib
target diz em qual versão de JavaScript a saída precisa rodar. Sintaxe mais nova que o target é reescrita: com "target": "es2017", um class field ou um ??= vira código mais antigo. O target mais baixo que o TypeScript 7 aceita é es2015 (es6); o es5 foi removido.
O target não adiciona APIs de runtime que faltam. Descrevê-las é trabalho do lib, e fornecê-las é trabalho de um polyfill. O lib diz ao verificador quais objetos e métodos nativos existem. O padrão dele acompanha o target e também inclui os tipos do DOM do navegador, então document passa na verificação mesmo em um projeto Node.js, a menos que você defina o lib. Usar um método de um padrão mais novo que o seu lib é erro de compilação:
src/b.ts(1,24): error TS2550: Property 'toSorted' does not exist on type 'number[]'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2023' or later.
Os exemplos executáveis destas docs usam o lib es2022, então toSorted, Object.groupBy e os novos métodos de Set não estão disponíveis neles.
module e moduleResolution
O module tem três valores sensatos para código novo:
nodenext: para código que o Node.js roda. Cada arquivo é ESM ou CommonJS de acordo com a extensão (.mts,.cts) ou o"type"dopackage.jsonmais próximo, exatamente como o Node decide. Imports relativos em módulos ES precisam de extensão, escrita como.jsmesmo que o fonte seja.ts:import { add } from "./math.js". OmoduleResolutionacompanha automaticamente.preserve: para código que passa por um bundler. Os imports ficam como foram escritos, e a resolução usa as regrasbundler, que permitem imports sem extensão.esnext: saída em módulo ES simples, com resoluçãobundler. É o padrão quandomodulenão está definido.
Um primeiro erro comum com a configuração do tsc --init vem do npm init -y gravar "type": "commonjs":
src/main.ts(1,10): error TS1295: ECMAScript imports and exports cannot be written in a CommonJS file under 'verbatimModuleSyntax'.
O arquivo usa import, mas o package.json diz que o projeto é CommonJS. Troque o "type" para "module". A página sobre módulos trata de imports e exports em detalhe.
strict e as opções de verificação
"strict": true liga um grupo de verificações de uma vez: noImplicitAny, strictNullChecks, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, strictBuiltinIteratorReturn, noImplicitThis e useUnknownInCatchVariables. É o padrão no TypeScript 7 e está ligado em todos os exemplos executáveis daqui. Código escrito para o strict mode faz o narrowing antes de usar um valor que pode faltar:
Sem anotação, o tipo de um parâmetro não pode ser inferido, e o noImplicitAny aponta isso:
index.ts(2,17): error TS7006: Parameter 'n' implicitly has an 'any' type.
O strict não inclui todas as verificações úteis. Duas que valem a pena adicionar são noUncheckedIndexedAccess (um elemento de array ou uma busca em index signature tem tipo T | undefined) e exactOptionalPropertyTypes (uma propriedade opcional não pode receber undefined explicitamente); o tsc --init liga as duas. Verificações de estilo como noUnusedLocals, noImplicitReturns e noFallthroughCasesInSwitch vêm desligadas por padrão.
Quais arquivos: include, exclude, rootDir, outDir
Sem include ou files, o projeto contém todo arquivo .ts, .tsx e .d.ts da pasta e das subpastas. O node_modules sempre fica de fora. O outDir também fica de fora, mas só enquanto você não define exclude: uma lista exclude própria substitui esse padrão, então inclua a pasta de saída nela. include e exclude aceitam padrões glob:
{
"include": ["src", "scripts/**/*.ts"],
"exclude": ["src/**/*.test.ts"]
}
O exclude só filtra o que o include encontrou. Um arquivo importado por outro arquivo incluído continua sendo compilado.
O outDir é para onde vai a saída, e o rootDir é a parte da árvore de fontes espelhada nela: com "rootDir": "./src", src/api/users.ts vira dist/api/users.js. Defina os dois. O rootDir tem como padrão a pasta que contém o tsconfig.json, então, se você definir só o outDir com os fontes em src, o TypeScript 7 para com:
error TS5011: The common source directory of 'tsconfig.json' is './src'. The 'rootDir' setting must be explicitly set to this or another path to adjust your output's file layout.
Opções de saída: noEmit, declaration, sourceMap
"noEmit": truetransforma otscem um verificador de tipos puro. Use quando outra ferramenta (um bundler, otsx, o type stripping do Node.js) gera o JavaScript."declaration": truegrava um arquivo.d.tspor módulo, os tipos sem o código. Bibliotecas precisam disso para que os usuários recebam os tipos. OdeclarationMapadiciona mapas para o "ir para a definição" levar ao fonte.ts."sourceMap": truegrava arquivos.js.mappara que depuradores e stack traces apontem para as linhas do.ts."noEmitOnError": truenão grava nada enquanto houver erros de tipo. Sem ele, otscreporta os erros e grava o JavaScript mesmo assim.
types, esModuleInterop e skipLibCheck
types lista os pacotes @types carregados globalmente, sem import. O padrão do TypeScript 7 é uma lista vazia, então depois de npm install --save-dev @types/node você também adiciona "types": ["node"]; senão process e require continuam desconhecidos (error TS2591: Cannot find name 'process'). Test runners com funções globais, como o describe do Jest, entram na mesma lista.
O esModuleInterop faz imports default de pacotes CommonJS funcionarem (import express from "express"). Ele está sempre ligado no TypeScript 7, e definir false é um erro.
O skipLibCheck pula a verificação de tipos dos arquivos de declaração. Ele acelera os builds e evita erros dentro de node_modules que você não consegue corrigir, com o custo de não notar conflitos entre os tipos de duas bibliotecas. A maioria dos projetos o liga.
Compartilhando configurações com extends
O extends carrega outra configuração e deixa este arquivo sobrescrever partes dela:
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist"
},
"include": ["src"]
}
As compilerOptions são mescladas opção por opção, enquanto include, exclude e files da base são substituídos, não mesclados, se este arquivo os definir. Caminhos no arquivo base são resolvidos em relação ao próprio arquivo base. O extends também aceita um pacote: o projeto @tsconfig/bases publica um por ambiente, por exemplo npm install --save-dev @tsconfig/node24 e depois "extends": "@tsconfig/node24/tsconfig.json".
Para ver as configurações finais depois de aplicar todos os extends e padrões, rode:
npx tsc --showConfig
Opções removidas no TypeScript 7
O TypeScript 6 marcou estas configurações como obsoletas e o TypeScript 7 as removeu. Uma configuração que ainda usa alguma falha com error TS5108 ou TS5102, por exemplo Option 'baseUrl' has been removed. Please remove it from your configuration.
| Configuração removida | Use no lugar |
|---|---|
"target": "es5" | es2015 ou posterior |
"moduleResolution": "node" (node10) ou "classic" | nodenext ou bundler |
"module": "amd", "umd", "system", "none" | nodenext, esnext ou preserve, e um bundler para outros formatos |
baseUrl | Entradas de paths relativas ao arquivo tsconfig |
outFile | Um bundler |
downlevelIteration | Nada: targets a partir de es2015 suportam iteração nativamente |
"esModuleInterop": false, "allowSyntheticDefaultImports": false, "alwaysStrict": false | Remova a linha; essas opções estão sempre ligadas |
A página do TypeScript 7 lista as outras mudanças dessa versão, incluindo os novos padrões que esta tabela não cobre.
Perguntas frequentes
O que é o tsconfig.json?
É o arquivo de configuração de um projeto TypeScript. A presença dele marca a pasta como raiz do projeto, compilerOptions define como o compilador verifica e gera o código, e include, exclude ou files dizem quais arquivos fazem parte do projeto. Rodar tsc sem argumentos lê esse arquivo.
Como criar um arquivo tsconfig.json?
Rode npx tsc --init na pasta do projeto (com o TypeScript instalado). Ele grava um tsconfig.json com configurações recomendadas e um comentário em cada opção. Você também pode escrever o arquivo à mão; {} é um tsconfig válido que usa todos os padrões.
Qual valor usar em target no tsconfig?
A versão mais antiga de JavaScript em que o seu código precisa rodar. Para o Node.js atual, es2022 ou posterior é seguro; o padrão do TypeScript 7 é es2025. O target controla qual sintaxe nova é reescrita para engines antigas e também escolhe o lib padrão, o conjunto de APIs nativas que o verificador conhece.
Qual é a diferença entre module e moduleResolution?
module decide o tipo de código de módulo que o compilador gera (ES import/export ou CommonJS require) e quais regras de módulo valem. moduleResolution decide como um caminho de import como "./utils.js" ou "lodash" é encontrado no disco. Use nodenext para código que o Node.js roda e module: "preserve" (que implica resolução bundler) para código que passa por um bundler.
O tsconfig.json pode ter comentários?
Sim. O compilador o lê como JSON com comentários: comentários // e /* */ e vírgulas no final são permitidos, e é por isso que o tsc --init gera um arquivo cheio de opções comentadas. Outras ferramentas que o leem como JSON estrito podem falhar com eles.