Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 21, 2025

The "never nullish" diagnostic (TS2881) was not reported when the left operand of a ?? operator was wrapped in parentheses, causing valid errors to be silently ignored.

const x: { y: string | undefined } | undefined = undefined as any;

const foo = x?.y ?? `oops` ?? "";      // Error reported ✓
const bar = (x?.y ?? `oops`) ?? "";    // Error NOT reported (bug)

Changes

  • checker.ts: Modified isNotWithinNullishCoalesceExpression to use walkUpOuterExpressions() instead of directly checking node.parent, allowing it to traverse through parentheses and other outer expressions to find the actual parent binary expression

  • Tests: Added neverNullishThroughParentheses.ts test case and updated predicateSemantics.ts baseline to include additional errors now correctly detected

Impact

The fix catches previously missed errors in cases like:

  • (expr ?? value) ?? fallback - now detects never-nullish value
  • opt ?? (null ?? 1) - now detects never-nullish 1
  • Multiple levels of parenthesis nesting
Original prompt

This section details on the original issue you should resolve

<issue_title>"Never nullish" checks miss "never nullish" through pareans</issue_title>
<issue_description>### 🔎 Search Terms

never nullish parenthesis

🕗 Version & Regression Information

  • This changed between versions 5.8 and 5.9

⏯ Playground Link

https://www.typescriptlang.org/play/?#code/MYewdgzgLgBAHgLhgbxgTydATgSzAcxgB8YBXMAEwFMAzPKimAX2LMtvsYF43q6wGMAIYRhYNAG4AsAChZs0JFg0QIGDzgB+AHRoYmzTAAGqgA4Qj+wwCJr0mYugwARkKzqYACi26rxsxYAlH629grgECAANlTaUSD4nioggfaO0bHxia5YqUA

💻 Code

const x: { y: string | undefined } | undefined = undefined as any;


const foo = x?.y ?? `oops` ?? "";
const bar = (x?.y ?? `oops`) ?? "";

console.log(foo);
console.log(bar);

🙁 Actual behavior

Error on foo's expression, no error on bar's.

🙂 Expected behavior

Error on both; both expressions are guaranteed to be "oops".

Additional information about the issue

No response</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@typescript-bot typescript-bot added For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Nov 21, 2025
Copilot AI and others added 2 commits November 21, 2025 05:17
Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix never nullish checks to include parenthesis cases Fix "never nullish" diagnostic missing expressions wrapped in parentheses Nov 21, 2025
Copilot AI requested a review from jakebailey November 21, 2025 05:42
Copilot finished work on behalf of jakebailey November 21, 2025 05:42
@RyanCavanaugh
Copy link
Member

@typescript-bot test this
@typescript-bot pack this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Nov 24, 2025

Starting jobs; this comment will be updated as builds start and complete.

Command Status Results
pack this ✅ Started ✅ Results
test top400 ✅ Started ✅ Results
user test this ✅ Started ✅ Results
run dt ✅ Started ✅ Results
perf test this faster ✅ Started 👀 Results

@typescript-bot
Copy link
Collaborator

typescript-bot commented Nov 24, 2025

Hey @RyanCavanaugh, I've packed this into an installable tgz. You can install it for testing by referencing it in your package.json like so:

{
    "devDependencies": {
        "typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/166605/artifacts?artifactName=tgz&fileId=A2C1143A3223567266A61A051AE7DFAA71E2A7FAC36CCA19230F756A7766160902&fileName=/typescript-6.0.0-insiders.20251124.tgz"
    }
}

and then running npm install.


There is also a playground for this build and an npm module you can use via "typescript": "npm:@typescript-deploys/pr-build@6.0.0-pr-62789-2".;

@typescript-bot
Copy link
Collaborator

Hey @RyanCavanaugh, the results of running the DT tests are ready.

Everything looks the same!

You can check the log here.

@typescript-bot
Copy link
Collaborator

@RyanCavanaugh Here are the results of running the user tests with tsc comparing main and refs/pull/62789/merge:

There were infrastructure failures potentially unrelated to your change:

  • 1 instance of "Git clone failed"

Otherwise...

Everything looks good!

@typescript-bot
Copy link
Collaborator

@RyanCavanaugh
The results of the perf run you requested are in!

Here they are:

tsc

Comparison Report - baseline..pr
Metric baseline pr Delta Best Worst p-value
Compiler-Unions - node (v18.15.0, x64)
Errors 1 1 ~ ~ ~ p=1.000 n=6
Symbols 62,370 62,370 ~ ~ ~ p=1.000 n=6
Types 50,386 50,386 ~ ~ ~ p=1.000 n=6
Memory used 193,841k (± 0.81%) 194,569k (± 0.96%) ~ 192,634k 196,301k p=0.378 n=6
Parse Time 1.30s (± 0.76%) 1.30s (± 0.58%) ~ 1.29s 1.31s p=1.000 n=6
Bind Time 0.75s 0.75s (± 0.54%) ~ 0.74s 0.75s p=0.405 n=6
Check Time 9.90s (± 0.46%) 9.89s (± 0.33%) ~ 9.84s 9.94s p=0.935 n=6
Emit Time 2.73s (± 0.79%) 2.74s (± 1.12%) ~ 2.70s 2.78s p=0.573 n=6
Total Time 14.68s (± 0.35%) 14.68s (± 0.15%) ~ 14.66s 14.72s p=0.810 n=6
angular-1 - node (v18.15.0, x64)
Errors 2 2 ~ ~ ~ p=1.000 n=6
Symbols 956,047 956,047 ~ ~ ~ p=1.000 n=6
Types 415,881 415,881 ~ ~ ~ p=1.000 n=6
Memory used 1,255,006k (± 0.00%) 1,255,010k (± 0.00%) ~ 1,254,967k 1,255,062k p=0.936 n=6
Parse Time 6.52s (± 0.76%) 6.56s (± 0.73%) ~ 6.47s 6.61s p=0.332 n=6
Bind Time 1.96s (± 0.21%) 1.96s (± 0.32%) ~ 1.95s 1.97s p=0.673 n=6
Check Time 32.40s (± 0.25%) 32.39s (± 0.32%) ~ 32.26s 32.52s p=0.873 n=6
Emit Time 14.87s (± 0.27%) 14.90s (± 0.28%) ~ 14.83s 14.95s p=0.261 n=6
Total Time 55.75s (± 0.17%) 55.80s (± 0.15%) ~ 55.69s 55.95s p=0.173 n=6
mui-docs - node (v18.15.0, x64)
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 2,717,214 2,717,214 ~ ~ ~ p=1.000 n=6
Types 937,297 937,297 ~ ~ ~ p=1.000 n=6
Memory used 3,044,183k (± 0.00%) 3,044,181k (± 0.00%) ~ 3,044,113k 3,044,274k p=0.689 n=6
Parse Time 8.54s (± 0.29%) 8.54s (± 0.40%) ~ 8.51s 8.60s p=0.936 n=6
Bind Time 2.32s (± 0.35%) 2.32s (± 0.54%) ~ 2.30s 2.33s p=0.558 n=6
Check Time 93.14s (± 0.31%) 93.14s (± 0.23%) ~ 92.80s 93.35s p=1.000 n=6
Emit Time 0.31s (± 2.44%) 0.31s (± 1.68%) ~ 0.30s 0.31s p=0.784 n=6
Total Time 104.30s (± 0.29%) 104.31s (± 0.19%) ~ 104.02s 104.52s p=1.000 n=6
self-build-src - node (v18.15.0, x64)
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 1,251,545 1,251,546 +1 (+ 0.00%) ~ ~ p=0.001 n=6
Types 259,756 259,756 ~ ~ ~ p=1.000 n=6
Memory used 2,514,295k (± 7.47%) 2,514,152k (±11.83%) ~ 2,392,166k 3,121,774k p=0.298 n=6
Parse Time 5.17s (± 1.25%) 5.19s (± 1.07%) ~ 5.11s 5.25s p=0.689 n=6
Bind Time 1.83s (± 0.98%) 1.81s (± 0.81%) ~ 1.79s 1.82s p=0.067 n=6
Check Time 35.31s (± 0.77%) 35.47s (± 0.47%) ~ 35.33s 35.80s p=0.872 n=6
Emit Time 3.03s (± 0.74%) 2.99s (± 2.16%) ~ 2.92s 3.11s p=0.065 n=6
Total Time 45.34s (± 0.47%) 45.48s (± 0.40%) ~ 45.32s 45.78s p=0.689 n=6
self-build-src-public-api - node (v18.15.0, x64)
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 1,251,545 1,251,546 +1 (+ 0.00%) ~ ~ p=0.001 n=6
Types 259,756 259,756 ~ ~ ~ p=1.000 n=6
Memory used 3,187,568k (± 0.03%) 3,066,916k (± 9.64%) ~ 2,462,855k 3,187,984k p=0.936 n=6
Parse Time 6.81s (± 0.77%) 6.77s (± 1.77%) ~ 6.58s 6.91s p=0.575 n=6
Bind Time 2.26s (± 2.19%) 2.26s (± 1.39%) ~ 2.21s 2.30s p=0.748 n=6
Check Time 43.13s (± 0.29%) 43.08s (± 0.27%) ~ 42.91s 43.24s p=0.575 n=6
Emit Time 3.52s (± 3.15%) 3.48s (± 2.15%) ~ 3.39s 3.58s p=0.630 n=6
Total Time 55.73s (± 0.47%) 55.59s (± 0.32%) ~ 55.34s 55.83s p=0.471 n=6
self-compiler - node (v18.15.0, x64)
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 264,177 264,178 +1 (+ 0.00%) ~ ~ p=0.001 n=6
Types 103,979 103,979 ~ ~ ~ p=1.000 n=6
Memory used 443,228k (± 0.01%) 443,189k (± 0.01%) ~ 443,124k 443,279k p=0.230 n=6
Parse Time 3.52s (± 0.93%) 3.52s (± 0.84%) ~ 3.48s 3.55s p=1.000 n=6
Bind Time 1.38s (± 1.64%) 1.38s (± 0.76%) ~ 1.36s 1.39s p=0.451 n=6
Check Time 19.21s (± 0.23%) 19.16s (± 0.37%) ~ 19.10s 19.28s p=0.171 n=6
Emit Time 1.54s (± 0.86%) 1.53s (± 1.04%) ~ 1.51s 1.55s p=0.498 n=6
Total Time 25.64s (± 0.24%) 25.59s (± 0.22%) ~ 25.52s 25.67s p=0.172 n=6
ts-pre-modules - node (v18.15.0, x64)
Errors 72 72 ~ ~ ~ p=1.000 n=6
Symbols 225,386 225,386 ~ ~ ~ p=1.000 n=6
Types 94,304 94,304 ~ ~ ~ p=1.000 n=6
Memory used 370,073k (± 0.02%) 370,054k (± 0.02%) ~ 369,950k 370,210k p=0.689 n=6
Parse Time 2.85s (± 0.92%) 2.84s (± 1.13%) ~ 2.81s 2.88s p=0.627 n=6
Bind Time 1.65s (± 1.37%) 1.64s (± 1.48%) ~ 1.60s 1.67s p=0.566 n=6
Check Time 16.65s (± 0.13%) 16.64s (± 0.13%) ~ 16.61s 16.67s p=0.373 n=6
Emit Time 0.00s 0.00s ~ ~ ~ p=1.000 n=6
Total Time 21.15s (± 0.20%) 21.12s (± 0.14%) ~ 21.08s 21.16s p=0.199 n=6
vscode - node (v18.15.0, x64)
Errors 11 12 🔻+1 (+ 9.09%) ~ ~ p=0.001 n=6
Symbols 4,051,172 4,051,172 ~ ~ ~ p=1.000 n=6
Types 1,275,269 1,275,269 ~ ~ ~ p=1.000 n=6
Memory used 3,837,189k (± 0.01%) 3,837,075k (± 0.00%) ~ 3,836,965k 3,837,318k p=0.575 n=6
Parse Time 15.60s (± 0.60%) 15.55s (± 0.40%) ~ 15.46s 15.63s p=0.377 n=6
Bind Time 5.24s (± 0.45%) 5.29s (± 0.85%) +0.05s (+ 0.89%) 5.25s 5.37s p=0.043 n=6
Check Time 113.01s (± 3.12%) 111.71s (± 2.99%) ~ 107.29s 117.31s p=0.575 n=6
Emit Time 40.74s (±15.04%) 41.24s (±25.40%) ~ 32.25s 60.13s p=0.810 n=6
Total Time 174.60s (± 5.18%) 173.79s (± 5.90%) ~ 164.16s 188.25s p=0.936 n=6
webpack - node (v18.15.0, x64)
Errors 40 40 ~ ~ ~ p=1.000 n=6
Symbols 380,027 380,027 ~ ~ ~ p=1.000 n=6
Types 166,673 166,673 ~ ~ ~ p=1.000 n=6
Memory used 539,669k (± 0.03%) 539,599k (± 0.02%) ~ 539,407k 539,726k p=0.173 n=6
Parse Time 4.62s (± 0.50%) 4.62s (± 0.47%) ~ 4.59s 4.65s p=0.747 n=6
Bind Time 2.00s (± 0.61%) 2.01s (± 1.02%) ~ 1.98s 2.04s p=0.254 n=6
Check Time 22.98s (± 1.04%) 23.01s (± 1.62%) ~ 22.66s 23.51s p=0.873 n=6
Emit Time 0.00s 0.00s ~ ~ ~ p=1.000 n=6
Total Time 29.60s (± 0.79%) 29.63s (± 1.27%) ~ 29.31s 30.17s p=0.810 n=6
xstate-main - node (v18.15.0, x64)
Errors 30 30 ~ ~ ~ p=1.000 n=6
Symbols 690,397 690,397 ~ ~ ~ p=1.000 n=6
Types 208,790 208,790 ~ ~ ~ p=1.000 n=6
Memory used 586,175k (± 0.02%) 586,122k (± 0.02%) ~ 585,995k 586,283k p=0.689 n=6
Parse Time 4.16s (± 0.87%) 4.17s (± 0.78%) ~ 4.13s 4.21s p=1.000 n=6
Bind Time 1.41s (± 0.73%) 1.41s (± 1.07%) ~ 1.39s 1.42s p=0.564 n=6
Check Time 20.77s (± 2.10%) 20.84s (± 1.78%) ~ 20.27s 21.15s p=0.575 n=6
Emit Time 0.00s (±154.76%) 0.00s (±154.76%) ~ 0.00s 0.01s p=1.000 n=6
Total Time 26.35s (± 1.66%) 26.42s (± 1.44%) ~ 25.88s 26.75s p=0.936 n=6
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • Compiler-Unions - node (v18.15.0, x64)
  • angular-1 - node (v18.15.0, x64)
  • mui-docs - node (v18.15.0, x64)
  • self-build-src - node (v18.15.0, x64)
  • self-build-src-public-api - node (v18.15.0, x64)
  • self-compiler - node (v18.15.0, x64)
  • ts-pre-modules - node (v18.15.0, x64)
  • vscode - node (v18.15.0, x64)
  • webpack - node (v18.15.0, x64)
  • xstate-main - node (v18.15.0, x64)
Benchmark Name Iterations
Current pr 6
Baseline baseline 6

Developer Information:

Download Benchmarks

@typescript-bot
Copy link
Collaborator

@RyanCavanaugh Here are the results of running the top 400 repos with tsc comparing main and refs/pull/62789/merge:

Everything looks good!

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

Labels

For Uncommitted Bug PR for untriaged, rejected, closed or missing bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

"Never nullish" checks miss "never nullish" through pareans

4 participants