Repository navigation
Add flow path for a falsy, non-nullish left operand of ?? and ??= - #65409
Open
Ryan Cavanaugh (RyanCavanaugh) with Copilot wants to merge 4 commits into
Open
Ryan Cavanaugh (RyanCavanaugh) with Copilot wants to merge 4 commits into
Ryan Cavanaugh (RyanCavanaugh) with Copilot wants to merge 4 commits into
Conversation
Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Fix false TS7027 and wrong narrowing with falsy left operand
Add flow path for a falsy, non-nullish left operand of Nov 9, 2026
?? and ??=
Ryan Cavanaugh (RyanCavanaugh)
marked this pull request as ready for review
November 9, 2026 19:56
Ryan Cavanaugh (RyanCavanaugh)
requested review from
Anders Hejlsberg (ahejlsberg)
and
a balanced review from Copilot
November 9, 2026 19:56
Member
|
Copilot 🚲 |
Contributor
Author
Could you clarify what you’d like me to do? Does 🚲 mean to address the earlier review findings? |
Member
|
Copilot do another "cycle" of work on this - you have high-priority comments, and failing CI! |
Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com>
Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com>
Contributor
Author
Added the missing compiler baselines and regression cases in 1ff6f2b; fixed parenthesized literal reachability and nested optional-chain side-effect routing in f89b8bb. |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The binder treats the left operand of
??and??=like the left operand of||. A non-nullish left operand goes to the true branch and a nullish one goes to the right operand. A left operand that is falsy but not nullish (false,0,"") makes the whole expression falsy without running the right operand, but the flow graph has no path for it. This causes false TS7027 errors and wrong narrowing in the false branch:Changes
ast/flow.go: adds theFlowFlagsNonNullishflag. Combined withFlowFlagsFalseCondition, it means the left operand of??/??=is falsy but not nullish.checker/flow.go:getTypeAtFlowConditionnarrows a condition with this flag to non-nullish first, then to falsy.binder.go: the newbindNullishCoalescingLeftadds a path from the left operand to a falsy target:FalseCondition|NonNullishcondition on the operand. It is skipped when the falsy target equals the true target, as in a??whose value is not used as a condition. There, the existing true condition already includes falsy non-nullish values.&&,||,??or!as the left operand: the label before the right operand also flows to the falsy target. This over-approximates, since some of those paths are nullish, but it never drops a real path.a ?? b ?? c: two new binder fields,currentNullishTargetandcurrentFalsyTarget, track the enclosing??. A??whose false target is the enclosing??'s pre-right label sends its falsy path to the outer falsy target. This keepscfrom being narrowed as ifacould be"".(a ?? b)?.c: a falsy, non-nullishacontinues the chain rather than short-circuiting it.createFlowCondition: atrueornullleft operand with the new flag is unreachable. This keeps the existing TS7027 fortrue ?? …andnull ?? ….Behavior change
The binder does not use types, so a left operand that can never be falsy (e.g.
a: object) also gets the new path. This matches whatwhile (a)already does. As a result,if (a ?? b) {} else { const r: false = b; }witha: object | undefinednow reports TS2322, asif (a && b)already does.New test:
tsc/testdata/tests/cases/compiler/controlFlowNullishCoalescingFalsyLeft.ts. It covers literals,??=, chains, logical left operands, nesting inside||, and optional-chain roots with no new false positives.??is falsy but not nullish #65402