Incremental Builds in TypeScript

Incremental builds cache information about your project so TypeScript only rechecks what changed. Learn how to enable them and when they help speed up compilation.

4 min read

Every time you run the TypeScript compiler, it reads every file in your project and checks every type. For a small project with a dozen files, this is nearly instant. For a large codebase with hundreds of files, it can take seconds or even minutes.

TypeScript incremental builds solve this by saving information about your project between compilations. When you change one file and rebuild, TypeScript knows exactly which other files depend on it and only rechecks those. Files that have not changed are skipped entirely.

How incremental builds work

When you enable the incremental flag, TypeScript writes a .tsbuildinfo file after each successful build. This file contains a map of your project: which files exist, when each was last modified, which files import which other files, and how types flow between them.

On the next build, TypeScript reads the .tsbuildinfo file and compares it to the current state of your source. Files whose modification time has not changed since the last build are assumed to be valid. Files that changed, and any files that import from them, are rechecked.

The result is that changing one utility function in a large project only rechecks that file and the files that import it, not every file in the project.

Enabling incremental builds

Turn it on with a single line in your tsconfig:

jsonjson
{
  "compilerOptions": {
    "incremental": true
  }
}

That is all you need. The first build after enabling it takes the same time as always. Every build after that is faster.

The .tsbuildinfo file is a build artifact, like .js output files. Add it to your .gitignore:

texttext
*.tsbuildinfo

You can control where the file is written with the tsBuildInfoFile option. This is useful if you want to centralize build artifacts or have multiple configs that should not share a cache.

Incremental builds and composite projects

The incremental flag is closely related to the composite flag used for project references. When you set composite to true for a sub-project, incremental is automatically enabled for that project.

Project references let you split a large codebase into smaller pieces that build independently. Each sub-project gets its own .tsbuildinfo file. When you change code in one sub-project, only that project and the projects that depend on it rebuild.

For most single-package projects, you do not need composite. Just enable incremental and you get the caching benefit without the project reference infrastructure.

When incremental builds help most

Incremental builds shine in development workflows where you rebuild frequently after small changes. Running tsc --watch with incremental enabled means each save triggers a near-instant rebuild because only the changed file and its dependents are checked.

They also help in CI when your CI pipeline caches the .tsbuildinfo file. On the first CI run, the build takes full time. On subsequent runs, if only a few files changed between commits, the cached .tsbuildinfo file lets TypeScript skip most of the work.

Incremental builds provide the most value in medium to large projects. If your project has fewer than 50 files, the overhead of reading and writing the .tsbuildinfo file might cancel out the savings. Test it on your project: if the warm build time is noticeably faster, keep it on.

Limitations

The .tsbuildinfo cache is invalidated when TypeScript itself is upgraded. A new compiler version always performs a full rebuild the first time, then resumes caching on subsequent builds.

The cache also does not survive a clean checkout. If you clone the repository and run tsc, the first build is cold because there is no .tsbuildinfo file yet. This is why CI caching is important for larger projects.

Incremental builds speed up both type-checking and emit, not just type-checking. The .tsbuildinfo file records enough information for tsc to skip re-emitting a file's .js output when nothing relevant to it changed, not only to skip rechecking it. A plain tsc run with no incremental flag has no memory of the previous build, so it re-checks and re-emits every included file from scratch every time.

For more on compiler performance and the tools available to diagnose slow builds, see the TypeScript compiler explained. To understand other performance-related options in your config, read about important tsconfig options explained.

Rune AI

Rune AI

Key Insights

  • Enable incremental: true to make TypeScript cache project structure between builds.
  • .tsbuildinfo files store information about which files depend on which other files.
  • Subsequent builds only recheck files that actually changed and their dependents.
  • Use tsBuildInfoFile to control where the cache file is written.
  • Incremental is enabled by default when composite is on for project references.
RunePowered by Rune AI

Frequently Asked Questions

Do incremental builds affect the output JavaScript?

No. Incremental builds only change how fast TypeScript type-checks your project. The emitted .js files are identical whether incremental is on or off. The .tsbuildinfo files it creates are not part of your runtime code.

Where should I store .tsbuildinfo files?

By default they appear next to your tsconfig.json or in your outDir. You can control the location with the tsBuildInfoFile option. Add .tsbuildinfo to your .gitignore since these files are build artifacts.

When do incremental builds not help?

On the first build after a clean checkout, there is no cache to use. They also provide less benefit in very small projects where a full build is already fast. The biggest wins come in medium to large projects with many files.

Conclusion

Incremental builds are a low-effort performance win for most projects. Enable the incremental flag, add .tsbuildinfo to your .gitignore, and TypeScript will skip rechecking files that have not changed. In large codebases, the difference between a cold build and a warm incremental build can be dramatic.