Skip to content

Fix JSX parsing failures in ternary expressions with nested JSX attributes#2937

Draft
Copilot wants to merge 3 commits intomainfrom
copilot/fix-jsx-parsing-issue
Draft

Fix JSX parsing failures in ternary expressions with nested JSX attributes#2937
Copilot wants to merge 3 commits intomainfrom
copilot/fix-jsx-parsing-issue

Conversation

Copy link
Contributor

Copilot AI commented Feb 28, 2026

Valid TSX with ternary expressions containing parenthesized identifiers in the true branch and nested JSX with object literals in the false branch produces 30 spurious syntax errors. Standard tsc parses this correctly.

{!isLogin ? (
  DEFAULT_NULL_VALUE
) : (
  <HoverCardText
    label={<div>{nf(1, { precision: 2, rounding: 'down' })}</div>}
  ></HoverCardText>
)}

Root cause

During speculative arrow function parsing, (DEFAULT_NULL_VALUE) : is tried as an arrow signature with return type annotation. The return type parser encounters (<div ... and interprets < as the start of type parameters for a function type. This produces a FunctionType node with a missing parameter list (no ( found).

TypeScript's typeHasArrowFunctionBlockingParseError catches this via isMissingList(parameters) and aborts the speculative parse. The Go port was missing this check, so the speculative parse incorrectly committed, consuming JSX tokens.

Changes

  • ast.go: Add isMissing field to NodeList with accessor methods, porting TS's MissingList concept
  • parser.goparseParameters: Mark the parameter list as missing when ( is not found
  • parser.gotypeHasArrowFunctionBlockingParseError: Split FunctionType/ConstructorType from ParenthesizedType case and add the missing parameter list check, matching the TS reference:
case ast.KindFunctionType, ast.KindConstructorType:
    return node.FunctionLikeData().Parameters.IsMissing() ||
        typeHasArrowFunctionBlockingParseError(node.Type())
case ast.KindParenthesizedType:
    return typeHasArrowFunctionBlockingParseError(node.Type())
Original prompt

This section details on the original issue you should resolve

<issue_title>[bug] JSX parsing fails with spurious syntax errors in ternary expression with nested JSX and multi-property object literals</issue_title>
<issue_description>## Bug Report

tsgo version

7.0.0-dev.20260227.1

Reproduction

This is a fully self-contained reproduction with no external dependencies.

tsconfig.json:

{
  "compilerOptions": {
    "jsx": "preserve",
    "noEmit": true
  }
}

repro.tsx:

import { memo } from 'react'

const HoverCardText = (p: { label: any; text: any; className?: string }) => null
const DEFAULT_NULL_VALUE = '--'
const t = (key: string, params?: Record<string, any>) => key
const nf = (v: any, opts: { precision: number; rounding: string }) => String(v)

export const Example = memo(function Example() {
  const isLogin = true

  return (
    <div>
      <div>{t('label')}</div>
      {!isLogin ? (
        DEFAULT_NULL_VALUE
      ) : (
        <HoverCardText
          className="test"
          label={
            <div>
              {t('some.key', {
                s1: nf(1, { precision: 2, rounding: 'down' }),
                s2: nf(2, { precision: 2, rounding: 'down' }),
              })}
            </div>
          }
          text={
            <div>
              {nf(0, { precision: 2, rounding: 'down' })} USDT
            </div>
          }
        ></HoverCardText>
      )}
    </div>
  )
})

Expected behavior

No syntax errors — the JSX is valid. Standard tsc (5.x) compiles this without any syntax issues (only reports the expected TS2307: Cannot find module 'react' error since React isn't installed).

Actual behavior

tsgo --noEmit reports 30 spurious syntax errors starting at the <HoverCardText JSX element, such as:
repro.tsx(18,11): error TS1005: ',' expected
repro.tsx(19,11): error TS1005: ',' expected
repro.tsx(21,15): error TS1005: '(' expected
repro.tsx(21,18): error TS1003: Identifier expected....
The parser appears to fail to recognize the JSX context and instead interprets JSX attributes as JavaScript object properties.

Key observations from investigation

Removing any one of these elements makes the bug disappear:
The ternary expression (? ... : ...)
The

{t('label')}
preceding the ternary
The second property in the object literal inside label (s2: nf(...))
Reducing the object literal to a single property inside the label JSX attribute makes it work.
Using a string literal ('--') instead of DEFAULT_NULL_VALUE in the ternary's truthy branch also makes it work.
tsc 5.x handles this code correctly in all cases.
The bug appears to involve the parser's JSX state management when processing deeply nested expressions (ternary → JSX → JSX attribute → JSX child → function call → object literal with multiple properties containing nested object literals).</issue_description>

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


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

…s and object literals

Add investigation notes and test case for a bug where JSX parsing produces
spurious syntax errors when:
1. A ternary expression has a parenthesized identifier in the truthy branch
2. The falsy branch contains JSX with multi-line attributes
3. JSX attributes contain nested JSX with function calls having multi-property object literals

Root cause: The speculative arrow function parser incorrectly succeeds when parsing
the ternary truthy branch '(x)' as an arrow function parameter list, consuming the
ternary ':' as a return type annotation colon, and then the type parsing error
recovery through JSX-like content coincidentally leaves the token at '{' (from
{a:1,b:2}), which passes the OpenBraceToken check for arrow function body.

The speculative parse commits to an incorrect interpretation and doesn't rewind,
causing cascading syntax errors for all subsequent JSX content.
Copilot AI changed the title [WIP] Fix JSX parsing errors in nested ternary expressions Fix JSX parsing failures in ternary expressions with nested JSX and multi-property object literals Feb 28, 2026
…ulti-property object literals

Port the missing `isMissingList(parameters)` check from TypeScript's
`typeHasArrowFunctionBlockingParseError` to the Go parser. When the
parser speculatively tries to parse `(identifier) :` as an arrow function
with a return type annotation, the return type parsing can interpret
`(<JSXElement ...)` as a parenthesized function type (since `<` starts
type parameters). If the function type's parameter list is "missing"
(no `(` found), TypeScript correctly detects this as a blocking parse
error and aborts the speculative parse. The Go port was missing this
check, causing the speculative parse to incorrectly succeed and consume
tokens from the JSX content.

Changes:
- Add `isMissing` field to `NodeList` to track missing lists
- Mark parameter lists as missing in `parseParameters` when `(` not found
- Split FunctionType/ConstructorType from ParenthesizedType in
  `typeHasArrowFunctionBlockingParseError` to add the missing list check

Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
Copilot AI changed the title Fix JSX parsing failures in ternary expressions with nested JSX and multi-property object literals Fix JSX parsing failures in ternary expressions with nested JSX attributes Feb 28, 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.

[bug] JSX parsing fails with spurious syntax errors in ternary expression with nested JSX and multi-property object literals

2 participants