Skip to content

--incremental keeps stale diagnostics after lib or target changes in tsconfig.json (7.0.2, regression from 6.0.3) #64552

Description

🔎 Search Terms

incremental lib change stale diagnostics, tsbuildinfo target change, incremental compilerOptions change not invalidated, incremental stale error lib, TS2550 incremental

🕗 Version & Regression Information

  • This changed between versions 6.0.3 and 7.0.2
  • 5.9.3 and 6.0.3: correct in every run of the script below
  • 7.0.2: wrong in 12 of 40 checked steps (every one of the 10 runs goes wrong)
  • 7.1.0-dev.20260930.4 (next): the stale error no longer occurs, but the stale pass still does (wrong in 6 of 40 checked steps)

⏯ Playground Link

Not applicable (needs incremental state across several tsc runs).

💻 Code

a.ts:

export const s = [3, 1, 2].toSorted(); // needs lib es2023 or later

The steps flip one option in tsconfig.json, running an incremental and a non-incremental check after each step. Self-contained script (sh repro.sh 7.0.2, needs node and npm):

#!/bin/sh
# usage: sh repro.sh [typescript version]   (default 7.0.2)
set -u
V=${1:-7.0.2}
D=$(mktemp -d); cd "$D" || exit 2
npm i -s --no-audit --no-fund "typescript@$V" >/dev/null || exit 2
TSC=./node_modules/.bin/tsc
$TSC --version
echo 'export const s = [3, 1, 2].toSorted();' > a.ts   # needs lib es2023+

cfg() { printf '{ "compilerOptions": { "target": "ESNext", "types": []%s }, "files": ["a.ts"] }\n' "$1" > tsconfig.json; }
step() {
  inc=$($TSC -p . --noEmit --incremental --tsBuildInfoFile t.tsbuildinfo --pretty false | grep -c 'error TS')
  cold=$($TSC -p . --noEmit --pretty false | grep -c 'error TS')
  echo "  $1: incremental $inc error(s), cold $cold error(s)"
}
for opt in ', "lib": ["ES2020"]' ', "target": "ES2020"'; do
  echo "option added then removed:$opt"
  for run in 1 2 3 4 5; do
    rm -f t.tsbuildinfo
    echo " run $run"
    cfg "";     step "1 without option"
    cfg "$opt"; step "2 with option   "
    cfg "$opt"; step "3 unchanged     "
    cfg "";     step "4 without option"
  done
done
cd /; rm -rf "$D"

🙁 Actual behavior

With 7.0.2 the result is not deterministic. Each run takes one of two shapes (excerpt of real output):

Version 7.0.2
option added then removed:, "lib": ["ES2020"]
 run 1
  1 without option: incremental 0 error(s), cold 0 error(s)
  2 with option   : incremental 1 error(s), cold 1 error(s)
  3 unchanged     : incremental 1 error(s), cold 1 error(s)
  4 without option: incremental 1 error(s), cold 0 error(s)
 run 2
  1 without option: incremental 0 error(s), cold 0 error(s)
  2 with option   : incremental 0 error(s), cold 1 error(s)
  3 unchanged     : incremental 0 error(s), cold 1 error(s)
  4 without option: incremental 0 error(s), cold 0 error(s)
  • Stale error (run 1): after the option is removed, the incremental check still reports
    a.ts(1,28): 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.
    and exits 1. A non-incremental check exits 0.
  • Stale pass (run 2): after the option is added, the incremental check reports no errors and exits 0. A non-incremental check reports TS2550 and exits 1.

In both shapes the wrong result persists on later runs with no further change. It clears only when the .tsbuildinfo is deleted.

Counts from one execution of the script on 7.0.2:

  • lib: 4 stale-error runs and 1 stale-pass run out of 5.
  • target: 4 stale-error runs and 1 stale-pass run out of 5.

On next (7.1.0-dev.20260930.4):

  • lib: 2 stale-pass runs out of 5.
  • target: 1 stale-pass run out of 5.

The outcome is also nondeterministic with --singleThreaded and with --checkers 1.

Changing strict or noImplicitAny (tested with export function f(x) { return x; }) is invalidated correctly on 7.0.2. The failure seems limited to options that change the set of lib files.

In the stale-pass case, the .tsbuildinfo written after step 2 has no semanticDiagnosticsPerFile entry for a.ts, so the file is recorded as checked with no errors. In the correct case, the same entry holds the TS2550 diagnostic.

🙂 Expected behavior

At every step, the incremental check reports the same diagnostics as a non-incremental check with the same tsconfig.json, as 5.9.3 and 6.0.3 do: 0, 1, 1, 0 errors at steps 1 to 4 in every run.

Additional information about the issue

  • OS: macOS 26.6.2 (25G83), arm64
  • Node 24.21.0, npm 11.19.0
  • typescript@7.0.2 with native binary @typescript/typescript-darwin-arm64@7.0.2 (tsc --version: Version 7.0.2)
  • Compared against typescript@5.9.3, typescript@6.0.3 and typescript@7.1.0-dev.20260930.4

Possibly related, both closed: #64025 (incremental diagnostics from a JSON module never cleared) and microsoft/typescript-go#4664 (incremental build misses a global-scope .d.ts change).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Needs InvestigationThis issue needs a team member to investigate its status.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions