| |

TypeScript 74 🔷 Incremental Builds and Performance

TypeScript’s type checking is expensive, and the cost grows with the codebase. A large project can take minutes to compile, and the editor’s language service can lag on every keystroke. The compiler’s performance is not a fixed cost — it is a set of tradeoffs that can be tuned. The incremental flag caches the results and rebuilds only the changed files, the project references split the monorepo into the parallelizable units, the skipLibCheck skips the declaration files’ internal checking, and the isolatedModules enables the file-by-file transpilation. The tsc --diagnostics reports the time spent in each phase, and the --extendedDiagnostics reports the finer details. This chapter covers the performance’s model, the incremental’s build, the project references’ incremental, the skipLibCheck, the isolatedModules, the assumeChangesOnlyAffectDirectDependencies, the tsc --diagnostics, the tsc --generateTrace, the memory’s tuning, and the patterns that keep the build fast.

Key point: The incremental: true enables the incremental compilation and writes the .tsbuildinfo file, which records the file’s hashes and the dependency graph. The subsequent builds recheck only the changed files and their dependents. The tsBuildInfoFile specifies the cache’s location. The composite: true implies the incremental: true and is required for the project references. The tsc --build mode builds the referenced projects in the topological’s order and caches each one. The skipLibCheck: true skips the type checking of the declaration files, which is a significant speedup for the projects with the many dependencies. The isolatedModules: true ensures the file-by-file transpilation, which is required for the bundlers and the transpilers. The tsc --diagnostics reports the time spent in each phase, and the --extendedDiagnostics reports the finer details.


The performance’s model

TypeScript’s compilation has the phases, and each phase has the cost. Understanding the phases is the first step to the optimization.

The parse’s phase. The compiler reads the source files and the declaration files, and it builds the syntax trees. The parse’s cost is the file’s size’s, and the file’s size’s is the source’s.

The bind’s phase. The compiler builds the symbol’s tables, and it associates the declarations with the symbols. The bind’s cost is the declarations’s, and the declarations’s is the scope’s.

The check’s phase. The compiler checks the types, and it resolves the types. The check’s cost is the types’s, and the types’s is the most’s.

The emit’s phase. The compiler emits the JavaScript and the declaration files. The emit’s cost is the output’s, and the output’s is the size’s.

Why the check’s phase matters. The check’s phase is the most’s, and the most’s is the type’s. The type’s is the complex’s, and the complex’s is the generics’s and the conditional’s. The two are the pair, and the pair is the cost’s.

Why the parse’s phase matters. The parse’s phase is the file’s, and the file’s is the input’s. The many’s files are the slow’s, and the slow’s is the parse’s. The two are the pair, and the pair is the cost’s.

Why the emit’s phase matters. The emit’s phase is the output’s, and the output’s is the size’s. The noEmit‘s is the skip’s, and the skip’s is the speedup’s. The two are the pair, and the pair is the optimization’s.

Why the phases matter. The phases are the model’s, and the model’s is the diagnosis’s. The tsc --diagnostics reports the phases’s, and the phases’s is the optimization’s. The two are the pair, and the pair is the performance’s.


The incremental flag

The incremental: true enables the incremental compilation and writes the .tsbuildinfo file.

{
  "compilerOptions": {
    "incremental": true,
    "tsBuildInfoFile": "./dist/.tsbuildinfo"
  }
}

The incremental: true is the flag’s, and the flag’s is the cache’s. The tsBuildInfoFile is the location’s, and the location’s is the ./dist/.tsbuildinfo‘s. The two are the pair, and the pair is the performance’s.

Why the incremental matters. The incremental is the cache’s, and the cache’s is the rebuild’s. The .tsbuildinfo records the file’s hashes and the dependency graph, and the subsequent builds recheck only the changed files. The two are the pair, and the pair is the speedup’s.

Why the .tsbuildinfo matters. The .tsbuildinfo is the cache’s, and the cache’s is the file’s. The file’s records the hashes’s, and the hashes’s is the changed’s detection’s. The two are the pair, and the pair is the incremental’s.

Why the tsBuildInfoFile matters. The tsBuildInfoFile is the location’s, and the location’s is the outDir‘s. The default’s is the outDir‘s, and the ./dist/.tsbuildinfo‘s is the explicit’s. The two are the pair, and the pair is the organization’s.

Why the incremental‘s rebuild matters. The incremental‘s rebuild is the fast’s, and the fast’s is the development’s. The first’s build is the slow’s, and the subsequent’s is the fast’s. The two are the pair, and the pair is the workflow’s.

Why the incremental‘s cache matters. The incremental‘s cache is the local’s, and the local’s is the gitignore’s. The .tsbuildinfo should be the gitignored’s, and the gitignored’s is the team’s. The two are the pair, and the pair is the practice’s.

Why the incremental should be the default. The incremental should be the default, and the default’s is the modern’s. The TypeScript 4.0’s is the incremental’s, and the incremental’s is the standard’s. The two are the pair, and the pair is the recommendation’s.

Why the incremental‘s CI matters. The incremental‘s CI is the cache’s, and the cache’s is the pipeline’s. The CI’s cache is the .tsbuildinfo‘s, and the .tsbuildinfo‘s is the fast’s. The two are the pair, and the pair is the performance’s.

Why the incremental‘s false’s matters. The incremental: false is the disable’s, and the disable’s is the rare’s. The CI’s fresh’s is the disable’s, and the disable’s is the correctness’s. The two are the pair, and the pair is the design’s.


The project references’ incremental

The composite: true implies the incremental: true, and the project references’ build is the per-project’s.

// packages/shared/tsconfig.json
{
  "compilerOptions": {
    "composite": true,
    "declaration": true,
    "rootDir": "./src",
    "outDir": "./dist"
  }
}

The composite: true is the referenced’s, and the referenced’s is the incremental’s. The declaration: true is the requirement’s, and the requirement’s is the consumer’s. The two are the pair, and the pair is the design’s.

Why the project references’ incremental matters. The project references’ incremental is the per-project’s, and the per-project’s is the parallel’s. The tsc --build builds the projects in the topological’s order, and the order’s is the parallelizable’s. The two are the pair, and the pair is the performance’s.

Why the tsc --build‘s cache matters. The tsc --build‘s cache is the per-project’s, and the per-project’s is the .tsbuildinfo‘s. The each project has the .tsbuildinfo, and the .tsbuildinfo‘s is the cache’s. The two are the pair, and the pair is the incremental’s.

Why the tsc --build‘s parallel matters. The tsc --build‘s parallel is the independent’s, and the independent’s is the fast’s. The tsc --build builds the independent projects in the parallel, and the parallel’s is the performance’s. The two are the pair, and the pair is the optimization’s.

Why the project references’ incremental matters for the CI. The project references’ incremental is the CI’s, and the CI’s is the pipeline’s. The tsc --build is the graph’s, and the graph’s is the cache’s. The two are the pair, and the pair is the performance’s.

Why the project references’ incremental matters for the editor. The project references’ incremental is the editor’s, and the editor’s is the language service’s. The editor’s reads the .tsbuildinfo‘s, and the .tsbuildinfo‘s is the fast’s. The two are the pair, and the pair is the productivity’s.

Why the assumeChangesOnlyAffectDirectDependencies matters. The assumeChangesOnlyAffectDirectDependencies is the performance’s, and the performance’s is the fast’s. The flag assumes the changes only affect the direct’s dependencies, and the assumption’s is the speedup’s. The two are the pair, and the pair is the tradeoff’s.

Why the assumeChangesOnlyAffectDirectDependencies should be the careful. The flag is the careful’s, and the careful’s is the correctness’s. The flag can miss the transitive’s, and the transitive’s is the bug’s. The two are the pair, and the pair is the risk’s.

Why the disableSourceOfProjectReferenceRedirect matters. The disableSourceOfProjectReferenceRedirect is the editor’s, and the editor’s is the performance’s. The flag uses the .d.ts‘s instead of the source’s, and the .d.ts‘s is the fast’s. The two are the pair, and the pair is the tradeoff’s.


The skipLibCheck

The skipLibCheck: true skips the type checking of the declaration files, which is a significant speedup for the projects with the many dependencies.

{
  "compilerOptions": {
    "skipLibCheck": true
  }
}

The skipLibCheck: true is the flag’s, and the flag’s is the declaration’s. The skipLibCheck skips the .d.ts‘s internal checking, and the internal’s is the library’s. The two are the pair, and the pair is the performance’s.

Why the skipLibCheck matters. The skipLibCheck is the 29% improvement’s, and the improvement’s is the significant’s. The skipLibCheck skips the node_modules’s .d.ts‘s, and the .d.ts‘s is the many’s. The two are the pair, and the pair is the speedup’s.

Why the skipLibCheck is safe. The skipLibCheck is the safe’s, and the safe’s is the consumer’s. The consumer’s code is still checked against the types, and the types’s is the safety’s. The two are the pair, and the pair is the design’s.

Why the skipLibCheck‘s tradeoff matters. The skipLibCheck‘s tradeoff is the declaration’s internal’s, and the internal’s is the library’s. The library’s internal’s is the library’s bug’s, and the bug’s is the not the consumer’s. The two are the pair, and the pair is the design’s.

Why the skipLibCheck should be the default. The skipLibCheck should be the default, and the default’s is the modern’s. The TypeScript 2.0’s is the skipLibCheck‘s, and the skipLibCheck‘s is the standard’s. The two are the pair, and the pair is the recommendation’s.

Why the skipLibCheck‘s exception matters. The skipLibCheck‘s exception is the declaration’s bug’s, and the bug’s is the rare’s. The skipLibCheck: false is the debugging’s, and the debugging’s is the specific’s. The two are the pair, and the pair is the diagnosis’s.

Why the skipLibCheck‘s node_modules matters. The skipLibCheck‘s node_modules is the many’s, and the many’s is the .d.ts‘s. The node_modules’s .d.ts‘s is the skip’s, and the skip’s is the performance’s. The two are the pair, and the pair is the optimization’s.

Why the skipLibCheck‘s local matters. The skipLibCheck‘s local is the project’s, and the project’s is the checked’s. The local’s .d.ts‘s is the checked’s, and the checked’s is the safety’s. The two are the pair, and the pair is the design’s.

Why the skipLibCheck‘s combination matters. The skipLibCheck‘s combination is the incremental‘s, and the incremental‘s is the fast’s. The two are the pair, and the pair is the performance’s.


The isolatedModules

The isolatedModules: true ensures the file-by-file transpilation, which is required for the bundlers and the transpilers.

{
  "compilerOptions": {
    "isolatedModules": true
  }
}

The isolatedModules: true is the flag’s, and the flag’s is the file-by-file’s. The isolatedModules catches the files’s that cannot be transpiled in the isolation, and the isolation’s is the bundler’s. The two are the pair, and the pair is the performance’s.

Why the isolatedModules matters. The isolatedModules is the bundler’s, and the bundler’s is the Vite’s and the esbuild’s. The Vite and the esbuild transpile the file-by-file, and the file-by-file’s is the fast’s. The two are the pair, and the pair is the performance’s.

Why the isolatedModules‘s errors matter. The isolatedModules‘s errors are the type-only’s, and the type-only’s is the export type‘s. The export { SomeType }‘s is the error’s, and the export type { SomeType }‘s is the fix’s. The two are the pair, and the pair is the design’s.

Why the isolatedModules‘s const enum‘s matters. The isolatedModules‘s const enum‘s is the error’s, and the error’s is the cross-file’s. The const enum‘s is the inlining’s, and the inlining’s is the file-by-file’s. The two are the pair, and the pair is the design’s.

Why the isolatedModules‘s verbatimModuleSyntax matters. The isolatedModules‘s verbatimModuleSyntax is the pair’s, and the pair’s is the modern’s. The verbatimModuleSyntax requires the explicit’s, and the explicit’s is the file-by-file’s. The two are the pair, and the pair is the design’s.

Why the isolatedModules matters for the transpiler. The isolatedModules is the transpiler’s, and the transpiler’s is the SWC’s and the Babel’s. The SWC and the Babel transpile the file-by-file, and the file-by-file’s is the fast’s. The two are the pair, and the pair is the performance’s.

Why the isolatedModules matters for the editor. The isolatedModules is the editor’s, and the editor’s is the language service’s. The editor’s uses the file-by-file’s, and the file-by-file’s is the fast’s. The two are the pair, and the pair is the productivity’s.

Why the isolatedModules should be the default. The isolatedModules should be the default, and the default’s is the modern’s. The bundler’s is the standard’s, and the standard’s is the Vite’s. The two are the pair, and the pair is the recommendation’s.


The diagnostics

The tsc --diagnostics reports the time spent in each phase, and the --extendedDiagnostics reports the finer details.

tsc --diagnostics
# Files:            1234
# Lines:           123456
# Nodes:           567890
# Identifiers:     234567
# Symbols:         123456
# Types:            56789
# Instantiations:   23456
# Memory used:     456789K
# I/O read:         1.23s
# I/O write:        0.45s
# Parse time:       2.34s
# Bind time:        0.89s
# Check time:       5.67s
# Emit time:        1.23s
# Total time:      10.13s

The tsc --diagnostics is the phases’s, and the phases’s is the diagnosis’s. The Files, the Lines, the Nodes, the Identifiers, the Symbols, the Types, the Instantiations are the counts’s. The Memory used, the I/O read, the I/O write, the Parse time, the Bind time, the Check time, the Emit time, the Total time are the timings’s. The two are the pair, and the pair is the optimization’s.

Why the --diagnostics matters. The --diagnostics is the diagnosis’s, and the diagnosis’s is the optimization’s. The Check time is the most’s, and the most’s is the focus’s. The two are the pair, and the pair is the performance’s.

Why the --extendedDiagnostics matters. The --extendedDiagnostics is the finer’s, and the finer’s is the detail’s. The --extendedDiagnostics reports the Instantiation count, the Assignability cache size, the Identity cache size, the Subtype cache size, the Strict subtype cache size. The two are the pair, and the pair is the diagnosis’s.

Why the --generateTrace matters. The --generateTrace is the trace’s, and the trace’s is the Chrome’s. The --generateTrace ./trace produces the trace’s, and the trace’s is the chrome://tracing‘s. The two are the pair, and the pair is the diagnosis’s.

Why the --generateTrace should be the use. The --generateTrace should be the use, and the use’s is the specific’s. The --generateTrace is the heavy’s, and the heavy’s is the diagnosis’s. The two are the pair, and the pair is the performance’s.

Why the --extendedDiagnostics‘s Instantiation count matters. The Instantiation count is the generic’s, and the generic’s is the complex’s. The high’s count is the slow’s, and the slow’s is the optimization’s. The two are the pair, and the pair is the diagnosis’s.

Why the --extendedDiagnostics‘s Types matters. The Types is the count’s, and the count’s is the memory’s. The high’s count is the memory’s, and the memory’s is the limit’s. The two are the pair, and the pair is the diagnosis’s.

Why the --diagnostics should be the CI’s. The --diagnostics should be the CI’s, and the CI’s is the regression’s. The --diagnostics‘s is the baseline’s, and the baseline’s is the change’s. The two are the pair, and the pair is the monitoring’s.


The memory’s tuning

The --maxNodeModuleJsDepth and the --maxOldSpaceSize are the memory’s.

The --maxNodeModuleJsDepth. The --maxNodeModuleJsDepth limits the depth of the node_modules‘s traversal, and the depth’s is the parse’s.

tsc --maxNodeModuleJsDepth 0

The --maxNodeModuleJsDepth 0 is the no-traverse’s, and the no-traverse’s is the fast’s. The default’s is the 2’s, and the 2’s is the balance’s. The two are the pair, and the pair is the performance’s.

Why the --maxNodeModuleJsDepth matters. The --maxNodeModuleJsDepth is the node_modules’s, and the node_modules’s is the many’s. The depth’s is the parse’s, and the parse’s is the cost’s. The two are the pair, and the pair is the optimization’s.

The --maxOldSpaceSize. The --maxOldSpaceSize sets the Node.js’s heap’s size, and the heap’s is the memory’s.

node --max-old-space-size=8192 node_modules/.bin/tsc

The --max-old-space-size=8192 is the 8GB’s, and the 8GB’s is the large’s. The default’s is the 2GB’s or the 4GB’s, and the 4GB’s is the common’s. The two are the pair, and the pair is the performance’s.

Why the --maxOldSpaceSize matters. The --maxOldSpaceSize is the large’s, and the large’s is the memory’s. The large’s project’s is the memory’s, and the memory’s is the limit’s. The two are the pair, and the pair is the tuning’s.

Why the memory’s tuning matters. The memory’s tuning is the large’s, and the large’s is the specific’s. The heap out of memory‘s is the error’s, and the error’s is the limit’s. The two are the pair, and the pair is the fix’s.

Why the memory’s tuning should be the careful. The memory’s tuning should be the careful, and the careful’s is the tradeoff’s. The memory’s is the resource’s, and the resource’s is the limit’s. The two are the pair, and the pair is the design’s.

Why the memory’s tuning’s CI matters. The memory’s tuning’s CI is the pipeline’s, and the pipeline’s is the memory’s. The CI’s is the limit’s, and the limit’s is the NODE_OPTIONS‘s. The two are the pair, and the pair is the configuration’s.


The performance’s patterns

The patterns are the common’s, and the common’s is the practice’s.

The pattern 1: the incremental and the skipLibCheck. The two are the baseline’s, and the baseline’s is the fast’s.

{
  "compilerOptions": {
    "incremental": true,
    "skipLibCheck": true
  }
}

The incremental and the skipLibCheck are the baseline’s, and the baseline’s is the fast’s. The two are the pair, and the pair is the start’s.

Why the pattern 1 matters. The pattern 1 is the baseline’s, and the baseline’s is the first’s. The incremental is the cache’s, and the skipLibCheck is the declaration’s. The two are the pair, and the pair is the fast’s.

The pattern 2: the project references. The project references are the monorepo’s, and the monorepo’s is the parallel’s.

{
  "references": [
    { "path": "packages/shared" },
    { "path": "packages/ui" }
  ]
}

The references is the graph’s, and the graph’s is the parallel’s. The tsc --build is the topological’s, and the topological’s is the fast’s. The two are the pair, and the pair is the performance’s.

Why the pattern 2 matters. The pattern 2 is the monorepo’s, and the monorepo’s is the scale’s. The tsc --build is the incremental’s, and the incremental’s is the fast’s. The two are the pair, and the pair is the design’s.

The pattern 3: the isolatedModules. The isolatedModules is the bundler’s, and the bundler’s is the file-by-file’s.

{
  "compilerOptions": {
    "isolatedModules": true
  }
}

The isolatedModules is the file-by-file’s, and the file-by-file’s is the fast’s. The Vite and the esbuild are the transpiler’s, and the transpiler’s is the file-by-file’s. The two are the pair, and the pair is the performance’s.

Why the pattern 3 matters. The pattern 3 is the bundler’s, and the bundler’s is the modern’s. The isolatedModules is the standard’s, and the standard’s is the Vite’s. The two are the pair, and the pair is the design’s.

The pattern 4: the --diagnostics. The --diagnostics is the monitoring’s, and the monitoring’s is the regression’s.

tsc --diagnostics

The --diagnostics is the phases’s, and the phases’s is the diagnosis’s. The --diagnostics is the monitoring’s, and the monitoring’s is the regression’s. The two are the pair, and the pair is the practice’s.

Why the pattern 4 matters. The pattern 4 is the monitoring’s, and the monitoring’s is the discipline’s. The --diagnostics is the baseline’s, and the baseline’s is the change’s. The two are the pair, and the pair is the performance’s.

The pattern 5: the --generateTrace. The --generateTrace is the deep’s, and the deep’s is the specific’s.

tsc --generateTrace ./trace

The --generateTrace is the trace’s, and the trace’s is the Chrome’s. The --generateTrace is the deep’s, and the deep’s is the specific’s. The two are the pair, and the pair is the diagnosis’s.

Why the pattern 5 matters. The pattern 5 is the deep’s, and the deep’s is the rare’s. The --generateTrace is the heavy’s, and the heavy’s is the specific’s. The two are the pair, and the pair is the performance’s.

Why the patterns matter. The patterns are the vocabulary’s, and the vocabulary’s is the fluency’s. The five are the common’s, and the common’s is the practice’s. The two are the pair, and the pair is the skill’s.


Complete Example Session

// ============================================
// PART 1: THE BASELINE'S CONFIG
// ============================================

// tsconfig.json
{
  "compilerOptions": {
    "target": "es2022",
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "strict": true,
    "incremental": true,
    "tsBuildInfoFile": "./dist/.tsbuildinfo",
    "skipLibCheck": true,
    "isolatedModules": true,
    "verbatimModuleSyntax": true
  }
}

// ============================================
// PART 2: THE PROJECT REFERENCES
// ============================================

// The monorepo's root tsconfig.json
{
  "files": [],
  "references": [
    { "path": "packages/shared" },
    { "path": "packages/ui" },
    { "path": "packages/app" }
  ]
}

// ============================================
// PART 3: THE INCREMENTAL'S BUILD
// ============================================

// The first build:
// tsc --build
// The full's, the slow's.

// The incremental:
// tsc --build
// The changed's, the fast's.

// ============================================
// PART 4: THE DIAGNOSTICS
// ============================================

tsc --diagnostics
# Files:            1234
# Lines:           123456
# Nodes:           567890
# Memory used:     456789K
# Parse time:       2.34s
# Bind time:        0.89s
# Check time:       5.67s
# Emit time:        1.23s
# Total time:      10.13s

// ============================================
// PART 5: THE EXTENDED DIAGNOSTICS
// ============================================

tsc --extendedDiagnostics
# The finer's details.

// ============================================
// PART 6: THE GENERATE TRACE
// ============================================

tsc --generateTrace ./trace
# The trace's, and the chrome://tracing's.

// ============================================
// PART 7: THE SKIP LIB CHECK
// ============================================

// tsconfig.json
{
  "compilerOptions": {
    "skipLibCheck": true
  }
}

// ============================================
// PART 8: THE ISOLATED MODULES
// ============================================

// tsconfig.json
{
  "compilerOptions": {
    "isolatedModules": true
  }
}

// ============================================
// PART 9: THE MEMORY'S TUNING
// ============================================

NODE_OPTIONS="--max-old-space-size=8192" tsc

// ============================================
// PART 10: WHAT NOT TO DO
// ============================================

// Don't use the incremental: false for the dev
// The slow's.

// Don't forget the skipLibCheck
// The slow's.

// Don't forget the isolatedModules
// The bundler's error.

// Don't use the --diagnostics in the CI's build
// The overhead's.

// Don't use the --generateTrace without the need
// The heavy's.

// Don't forget the .tsbuildinfo's gitignore
// The team's conflict.

The ten parts cover the baseline’s config, the project references, the incremental’s build, the diagnostics, the extended diagnostics, the generate trace, the skip lib check, the isolated modules, the memory’s tuning, and the anti-patterns.


Quick Reference

The Performance’s Flags

FlagPurpose
The incrementalThe incremental’s cache
The tsBuildInfoFileThe cache’s location
The compositeThe project’s reference
The skipLibCheckThe declaration’s skip
The isolatedModulesThe file-by-file’s
The assumeChangesOnlyAffectDirectDependenciesThe fast’s assumption
The disableSourceOfProjectReferenceRedirectThe editor’s

The tsc‘s Diagnostics

FlagPurpose
The --diagnosticsThe phases’s
The --extendedDiagnosticsThe finer’s
The --generateTraceThe Chrome’s trace

The Memory’s Options

OptionPurpose
The --maxNodeModuleJsDepthThe node_modules’s depth
The --max-old-space-sizeThe Node.js’s heap

The Patterns

PatternPurpose
The incremental + the skipLibCheckThe baseline’s
The project referencesThe monorepo’s
The isolatedModulesThe bundler’s
The --diagnosticsThe monitoring’s
The --generateTraceThe deep’s

The Phases

PhaseThe cost
The parseThe input’s size
The bindThe declarations
The checkThe types (the most)
The emitThe output’s size

The CI’s Cache

ItemPurpose
The .tsbuildinfoThe incremental’s cache
The node_modulesThe dependencies’s
The TypeScript’s versionThe compiler’s

Best Practices

✅ Do This:

// Use the incremental and the skipLibCheck
{ "incremental": true, "skipLibCheck": true }                   // ✅
// Use the tsBuildInfoFile
{ "tsBuildInfoFile": "./dist/.tsbuildinfo" }                    // ✅
// Use the project references for the monorepo
{ "references": [{ "path": "packages/shared" }] }               // ✅
// Use the isolatedModules for the bundler
{ "isolatedModules": true }                                     // ✅
# Use the tsc --build for the monorepo
tsc --build                                                    # ✅

# Use the --diagnostics for the monitoring
tsc --diagnostics                                              # ✅
// Use the verbatimModuleSyntax
{ "verbatimModuleSyntax": true }                                // ✅
# Use the NODE_OPTIONS for the memory
NODE_OPTIONS="--max-old-space-size=8192" tsc                   # ✅

❌ Don’t Do This:

// Don't use the incremental: false for the dev
{ "incremental": false }                                        // ⚠️
// Don't forget the skipLibCheck
{ "skipLibCheck": false }                                       // ⚠️
// Don't forget the isolatedModules
{ "isolatedModules": false }  // the bundler's error            // ⚠️
# Don't use the --diagnostics in the CI's build
tsc --diagnostics  # the overhead's                              // ⚠️
# Don't use the --generateTrace without the need
tsc --generateTrace ./trace  # the heavy's                       // ⚠️
# Don't forget the .tsbuildinfo's gitignore
# The team's conflict.

Common Pitfalls

PitfallProblemSolution
The incremental: falseThe slowThe incremental: true
The missing skipLibCheckThe slowThe skipLibCheck: true
The missing isolatedModulesThe bundler’s errorThe isolatedModules: true
The stale .tsbuildinfoThe wrong’sThe --force
The --generateTrace in the CIThe heavyThe only the diagnosis
The missing tsBuildInfoFileThe scatteredThe explicit’s
The heap out of memoryThe limitThe --max-old-space-size
The maxNodeModuleJsDepth‘s defaultThe slowThe 0’s

Real-World Examples

1. The baseline’s config

{ "incremental": true, "skipLibCheck": true }

2. The tsBuildInfoFile

{ "tsBuildInfoFile": "./dist/.tsbuildinfo" }

3. The project references

{ "references": [{ "path": "packages/shared" }] }

4. The isolatedModules

{ "isolatedModules": true }

5. The tsc –build

tsc --build

6. The diagnostics

tsc --diagnostics

7. The extended diagnostics

tsc --extendedDiagnostics

8. The generate trace

tsc --generateTrace ./trace

9. The memory

NODE_OPTIONS="--max-old-space-size=8192" tsc

10. The maxNodeModuleJsDepth

tsc --maxNodeModuleJsDepth 0

Visual: The Incremental’s Build

┌──────────────────────────────────────────────────────────┐
│  THE FIRST BUILD                                         │
│    The full's: 15s                                       │
│    The .tsbuildinfo is written.                          │
│                                                          │
├──────────────────────────────────────────────────────────┤
│  THE INCREMENTAL'S BUILD                                 │
│    The change in one file.                               │
│    The changed's + the dependents's: 1-3s                │
│    The rest's are the untouched.                         │
│                                                          │
│  The .tsbuildinfo is the cache's, and the cache's is the │
│  speedup's.                                              │
│                                                          │
└──────────────────────────────────────────────────────────┘

Visual: The Phases’s Time

┌──────────────────────────────────────────────────────────┐
│  THE TYPICAL'S PROJECT                                   │
│                                                          │
│  The parse:  2.34s  ████                                 │
│  The bind:   0.89s  ██                                   │
│  The check:  5.67s  ████████████  ← the most             │
│  The emit:   1.23s  ███                                  │
│                                                          │
│  THE CHECK'S PHASE IS THE FOCUS'S.                       │
│    The generics, the conditional's, the recursive's.     │
│                                                          │
│  The --diagnostics reports the phases's.                 │
│                                                          │
└──────────────────────────────────────────────────────────┘

Visual: The skipLibCheck’s Impact

┌──────────────────────────────────────────────────────────┐
│  WITHOUT skipLibCheck                                    │
│    The node_modules's .d.ts's are checked.               │
│    The time: 15s                                         │
│                                                          │
├──────────────────────────────────────────────────────────┤
│  WITH skipLibCheck                                       │
│    The node_modules's .d.ts's are skipped.               │
│    The time: 10s                                         │
│    The 29% improvement's.                                │
│                                                          │
│  The consumer's code is still checked.                   │
│                                                          │
└──────────────────────────────────────────────────────────┘

Visual: The Project References’ Parallel

┌──────────────────────────────────────────────────────────┐
│  THE SEQUENTIAL                                          │
│    The shared: 5s                                        │
│    The ui: 3s                                            │
│    The app: 2s                                           │
│    The total: 10s                                        │
│                                                          │
├──────────────────────────────────────────────────────────┤
│  THE PARALLEL'S                                          │
│    The shared: 5s                                        │
│    The ui and the app: the parallel's                    │
│    The total: 7s                                         │
│                                                          │
│  The tsc --build builds the independent projects in the  │
│  parallel.                                               │
│                                                          │
└──────────────────────────────────────────────────────────┘

Visual: The isolatedModules’s Role

┌──────────────────────────────────────────────────────────┐
│  THE BUNDLER'S (the Vite, the esbuild)                   │
│    The file-by-file's transpilation.                     │
│    The type's is the separate's.                         │
│                                                          │
├──────────────────────────────────────────────────────────┤
│  THE isolatedModules's                                   │
│    The file's must be the independently transpilable.    │
│    The export type's is the explicit's.                  │
│    The const enum's is the error's.                      │
│                                                          │
│  The isolatedModules is the bundler's requirement.       │
│                                                          │
└──────────────────────────────────────────────────────────┘

Summary

ItemValue
The incrementalThe incremental’s cache
The tsBuildInfoFileThe cache’s location
The compositeThe project’s reference
The skipLibCheckThe declaration’s skip
The isolatedModulesThe file-by-file’s
The --diagnosticsThe phases’s
The --extendedDiagnosticsThe finer’s
The --generateTraceThe Chrome’s
The --max-old-space-sizeThe heap’s
The --maxNodeModuleJsDepthThe depth’s

Key takeaways:

  • The incremental: true enables the incremental compilation — the .tsbuildinfo records the file’s hashes and the dependency graph, and the subsequent builds recheck only the changed files and their dependents
  • The tsBuildInfoFile specifies the cache’s location — the default is the outDir, and the explicit is the organization’s
  • The composite: true implies the incremental: true — it is required for the project references, and it enables the per-project’s cache
  • The tsc --build builds the referenced projects in the topological’s order — it caches each one, and the independent projects are the parallelizable’s
  • The skipLibCheck: true skips the declaration files’ internal checking — it is the 29% improvement’s, and the consumer’s code is still checked
  • The isolatedModules: true ensures the file-by-file transpilation — it is required for the Vite, the esbuild, the SWC, and the Babel
  • The tsc --diagnostics reports the phases’s time — the Parse, the Bind, the Check, the Emit, and the Total
  • The Check phase is the most’s — the generics and the conditional’s are the focus’s, and the --extendedDiagnostics reports the finer’s
  • The --generateTrace produces the Chrome’s trace — it is the deep’s diagnosis, and the heavy’s
  • The --max-old-space-size and the --maxNodeModuleJsDepth are the memory’s tuning — the large’s project’s is the memory’s limit, and the depth’s is the parse’s

Remember: TypeScript’s performance is a set of tradeoffs. The incremental and the skipLibCheck are the baseline’s, the project references are the monorepo’s, and the isolatedModules is the bundler’s. The --diagnostics is the diagnosis’s, and the --generateTrace is the deep’s. The check’s phase is the most’s, and the generics and the conditional’s are the focus’s. The performance’s is the discipline’s, and the discipline’s is the practice’s.


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!