Skip to content

fix(parser): correctly disambiguate typescript arrow return type in t…#17879

Closed
NssGourav wants to merge 1 commit intobabel:mainfrom
NssGourav:fix/parser-ts-ternary-arrow-disambiguation
Closed

fix(parser): correctly disambiguate typescript arrow return type in t…#17879
NssGourav wants to merge 1 commit intobabel:mainfrom
NssGourav:fix/parser-ts-ternary-arrow-disambiguation

Conversation

@NssGourav
Copy link
Copy Markdown

Fix TypeScript Arrow Function Disambiguation in Ternary Expressions

Summary

This pull request fixes a long-standing issue in the TypeScript parser where the ternary separator (:) was incorrectly interpreted as an arrow function return type annotation when the ternary consequent was a parenthesized expression.

Issue: #17140

The Problem

In valid TypeScript, the following code should parse correctly:

let _ = a ? (b) : c => 0;

However, the Babel parser's TypeScript plugin would misinterpret (b) : c => 0 as a single arrow function expression, leading to a "Unexpected token, expected ':'" error at the end of the ternary because it consumed the ternary separator as part of the arrow.

This was previously documented as a "known limitation" in Babel's test fixtures (specifically in arrow-ambiguity).

The Solution

The fix involves two key changes to the TypeScript parser plugin:

  1. Context Sensitivity for shouldParseArrow: Mirroring the logic for async arrows, shouldParseArrow now checks if the parser is currently inside a ternary consequent. If so, a colon encountered after a parenthesized expression is treated as a ternary separator rather than a return type annotation.
  2. State Management across Nesting: Since parseParenAndDistinguishExpression resets the inConditionalConsequent flag (which is necessary for async arrows inside nested expressions), we now track the state more precisely. We save the outer conditional state before resetting and check it specifically in shouldParseArrow. We also reset this tracking when entering an arrow function body, ensuring that typed arrows inside arrow bodies continue to be correctly parsed.

Changes

  • @babel/parser: Modified packages/babel-parser/src/plugins/typescript/index.ts to implement the disambiguation logic in shouldParseArrow, parseParenAndDistinguishExpression, and parseArrowExpression.
  • Tests:
    • Added a new test fixture for the reported issue in packages/babel-parser/test/fixtures/typescript/conditional/arrow-in-conditional-consequent/.
    • Updated 3 existing test fixtures that previously expected parse errors.
    • Updated 3 other fixtures to reflect the corrected AST structure for nested arrows in ternaries.

Verification

  • Baseline Check: Confirmed that the original issue reproduces as a failure.
  • Unit Tests: All 21,971 tests in @babel/parser pass with zero regressions.
  • Integration: Verified that the change correctly handles all edge cases including nested ternaries and async calls.

Fixes #17140.

…ernary consequent

Similar to how async arrows were handled, this ensures that a colon
encountered after a parenthesized expression in the consequent of
a ternary is not misinterpreted as an arrow function return type
annotation, as colons in ternary consequents are reserved for the
ternary separator.

We store the outer inConditionalConsequent flag before it gets
reset by parseParenAndDistinguishExpression so that shouldParseArrow
(which runs after the reset) can correctly identify when to reject
the arrow interpretation. We reset this stored flag when entering
an arrow function body to allow typed arrows inside arrow bodies
within a ternary consequent.

Fixes babel#17140.
@babel-bot
Copy link
Copy Markdown
Collaborator

Build successful! You can test your changes in the REPL here: https://babeljs.io/repl/build/61197

@NssGourav NssGourav closed this Apr 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ts] bug with arrow functions in ternaries

2 participants