tsconfig.json Generator: Presets, Validation and Explain

TypeScript reads tsconfig.json before it checks a single file: the compilerOptions block decides how strict the checks are, which module syntax is emitted, and where output lands. Copying a snippet from an old repo freezes those choices without explaining them. This generator starts you from a tested preset, validates the combinations that TypeScript rejects, and prints a config with plain-language comments on every flag.

Configure, validate, and export

Boolean flags
Generated tsconfig.json appears here.
Validation runs on every build.

How It Works

compilerOptions entries sort into three practical groups. Language and strictness flags such as strict, noImplicitOverride, and noUncheckedIndexedAccess decide which mistakes become compile errors. Emit flags such as target, declaration, sourceMap, and outDir decide what tsc writes to disk. Interop flags such as esModuleInterop, resolveJsonModule, and verbatimModuleSyntax decide how imports from CommonJS packages and JSON files are treated.

Above compilerOptions sit the top-level keys this page fills in for you. include holds globs such as src/**/* that choose which files join the program, and exclude removes paths from that set. When include is left out, the compiler picks up every file beside the config, quietly adding build output and fixtures to the check. A tight include keeps compile time and error lists short.

Some pairings are refused outright, and the validator repeats those refusals before you copy anything. Bundler resolution needs module set to Preserve or ES2015 and later. allowImportingTsExtensions needs noEmit or emitDeclarationOnly, because a .ts import suffix cannot survive a JavaScript emit. Composite projects cannot disable declaration emit, and sourceMap cannot pair with inlineSourceMap. Each clash is named in the issues box.

Before and After

Before (module and moduleResolution clash)
{
  "compilerOptions": {
    "module": "CommonJS",
    "moduleResolution": "Bundler"
  }
}
error: "moduleResolution": "Bundler" needs "module" set to Preserve or ES2015 and later. "CommonJS" is too old for it.
After (strict-base preset output)
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "strict": true,
    "noEmit": true
  },
  "include": ["src/**/*"]
}

Use Cases

Project type Starting point What to watch
Greenfield app on Vite or Next strict-base preset, include set to src/**/* noEmit stays on so the bundler owns output
Library published to npm library-publish preset with declaration and declarationMap Ship sources so declaration maps resolve for consumers
JavaScript repo being migrated allowJs first, then strict in a later pass Enable noUnusedLocals per pull request, not in bulk

Common Mistakes

  • Leaving skipLibCheck off on a large dependency tree. Checking every .d.ts file under node_modules can double CI time, and failures there usually belong to package authors.
  • Pairing module CommonJS with moduleResolution Bundler. The two settings assume different module syntax, so imports stop resolving.
  • Adding noUnusedLocals to a long-running codebase in one commit. A wall of dead-variable errors then buries the real regressions in the same pull request.
  • Setting outDir and rootDir to the same folder. Emitted JavaScript lands on top of the TypeScript sources, and the originals are gone.
  • Omitting include at a monorepo root. The compiler then walks build output and test fixtures, slowing the check with errors nobody meant to compile.

Best Practices

  • Start from one of the four presets and change one flag at a time. Each already pairs module with a resolution mode that accepts it.
  • Run tsc --noEmit in continuous integration on every pull request. The flag matches the noEmit checkbox and surfaces type errors without writing files.
  • Keep include narrow, usually src/**/*, and exclude dist, coverage, and generated folders. Narrow scope keeps compile time down and stack traces readable.
  • For published packages, pair declaration with declarationMap and ship the original sources beside the .d.ts files so consumers can jump to definitions.
  • Use project references with composite for multi-package repositories. Each package emits its own declarations and tsc -b reuses the build cache across projects.

Frequently Asked Questions

What does this page produce?

The build button prints a tsconfig.json body with a compilerOptions block, plus an include array when that field is filled. Comments above each flag match the lookup descriptions and can be toggled off. Empty text fields stay out of the output. Copy puts the text on the clipboard and Download saves a file named tsconfig.json.

Why start from a preset instead of copying an old config?

A copied file freezes choices made for another runtime and folder layout. Each preset pairs module with a resolution mode that accepts it, so the combination is valid before you touch a checkbox. Flip flags one at a time while the validator watches for clashes. The result is a config you can explain in review.

What does the validator check?

It repeats rules the compiler enforces, including Bundler resolution requiring module set to Preserve or ES2015 and later, allowImportingTsExtensions requiring noEmit or emitDeclarationOnly, and composite projects that cannot disable declaration. Softer findings arrive as warnings, such as outDir and rootDir pointing at the same folder or the legacy ES6 alias. Errors print in red and warnings in amber beneath a count line.

What turns on when strict is checked?

strict enables noImplicitAny, strictNullChecks, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, noImplicitThis, useUnknownInCatchVariables, and alwaysStrict in one switch. New codebases belong on strict because it catches null handling mistakes at the point they are written. The extra flags on this page, such as noUncheckedIndexedAccess and exactOptionalPropertyTypes, stay separate because they add work even in an already-strict project.

How do module and moduleResolution relate?

module decides the syntax written into emitted files, while moduleResolution decides how import specifiers become paths on disk. A Vite or webpack app pairs ESNext or Preserve with Bundler resolution. A real Node ESM package uses NodeNext on both sides. Pairing CommonJS output with Bundler resolution is the mismatch flagged first.

Should skipLibCheck be true?

For most apps and libraries, yes. Checking every declaration file under node_modules adds significant time on large dependency trees, and the problems found there usually belong to package authors. Turn it off briefly when you suspect a dependency has wrong types and need proof before filing an issue. All four presets here ship with it on.

Does any of my data leave the browser?

No. Preset mapping, validation, JSON generation, and the option lookup all run inside this tab with no network request. Nothing is written to local storage, so configs with internal paths or registry hosts stay on your machine. Closing the tab discards the controls and the last set of issues.