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.
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:
{
"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:
*.tsbuildinfoYou 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
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.
Frequently Asked Questions
Do incremental builds affect the output JavaScript?
Where should I store .tsbuildinfo files?
When do incremental builds not help?
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.
More in this topic
TypeScript Compiler Explained
The TypeScript compiler (tsc) turns .ts files into .js files while checking types. Learn what it does, how to use it, and how it fits your workflow.
Set Up ESLint for TypeScript
Install and configure ESLint with typescript-eslint to catch bugs and enforce consistent code style in your TypeScript project.
Pick Utility Type in TypeScript
Pick creates a new type by selecting only the properties you name from an existing type. Use it to build focused types for specific views, API responses, or function parameters.