Skip to content

Re-scan '<<' when parsing type arguments in heritage clauses, typeof queries, and JSX - #64716

Open
ahmad65saad wants to merge 3 commits into
microsoft:mainfrom
ahmad65saad:fix-shift-token-in-type-arguments
Open

ahmad65saad wants to merge 3 commits into
microsoft:mainfrom
ahmad65saad:fix-shift-token-in-type-arguments

Conversation

@ahmad65saad

@ahmad65saad ahmad65saad commented Oct 10, 2026 •

Copy link
Copy Markdown

Fixes #47410

The scanner produces << as a single token, and the parser is expected to call reScanLessThanToken() wherever a < may open a type argument list. parseTypeArgumentsOfTypeReference and tryParseTypeArgumentsInExpression already do this, but parseTypeArguments only checked for <. So when the first type argument itself starts with < (e.g. a generic function type), the list was never parsed and the rest was misread as a shift/relational expression:

declare const f: <T>(x: T) => T;
declare class B<F> {}
interface I<F> {}
interface J<F> {}

class C1 implements I<<T>(x: T) => T> {}   // error before, OK now
class C2 extends B<<T>(x: T) => T> {}      // error before, OK now
interface I2 extends J<<T>(x: T) => T> {}  // error before, OK now
type A1 = typeof f<<T>(x: T) => T>;        // error before, OK now
<Comp<<T>(x: T) => T> value={f} />;        // error before, OK now

const e = f<<T>(x: T) => T>;               // already worked (expression path re-scans)

Fix

  • parseTypeArguments now re-scans << before checking for <. This covers heritage clauses (parseExpressionWithTypeArguments) and JSX opening/self-closing elements. In those positions << can't be a left shift, so no valid code changes meaning. JSDoc @augments/@implements also go through parseTypeArguments, but the JSDoc scanner never produces << there, so they already worked; jsdocAugments_genericFunctionTypeArgument.ts pins that (its baselines are identical with and without this change).
  • typeof type queries are the exception: there << can legitimately be a left shift after a type in an expression, e.g. a as typeof x << 1, which compiles today. So in parseTypeQuery, when the token is <<, the type arguments are parsed speculatively (mark/rewind, same diagnostics-count check as skipParameterStart) and dropped if they don't parse cleanly.

Tests

New test parseGenericArrowRatherThanLeftShift2.tsx covers each position above, plus the instantiation/call expression forms, and ordinary << shift expressions (including inside JSX attributes and after as typeof / satisfies typeof) to make sure they still parse as shifts. The first commit adds the test with the old (erroring) baselines so the change is visible in the second commit's diff. No other baselines changed.

Note: the JSDoc test's baseline includes TS8023 (JSDoc '@augments A' does not match the 'extends A' clause). That also happens on main without this change: checkJSDocAugmentsTagMatchesExtends compares the two A<<T>(x: T) => T> types with isTypeIdenticalTo, and the separately written generic function types aren't identical. It's a checker issue unrelated to parsing, so I left it out of scope here.

  • npx hereby test ✅
  • npx hereby lint ✅
  • npx hereby check:format ✅
  • npx hereby validate ✅

This PR was written in part with the assistance of generative AI (Claude).

parseTypeArguments only checked for a '<' token, so a type argument list
whose first type starts with '<' (e.g. a generic function type) was scanned
as '<<' and never parsed. This affected heritage clauses, typeof type
queries, and JSX opening elements. Re-scan the token first, as the type
reference and expression paths already do.

In a typeof type query, '<<' can also be a left shift following a type in
an expression, as in `x as typeof y << 1`, which already compiles. There the
type arguments are parsed speculatively and only kept if they parse without
errors; otherwise the '<<' is left alone.

Fixes microsoft#47410
Copilot AI balanced review requested due to automatic review settings October 10, 2026 02:47
@typescript-automation typescript-automation Bot added the For Backlog Bug PRs that fix a backlog bug label Oct 10, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The newly affected JSDoc parsing path lacks regression coverage.

1 open finding
What changed in this PR

Fixes parsing of generic function types beginning with << in type arguments while preserving left-shift expressions.

Changes:

  • Re-scans << when parsing type arguments.
  • Speculatively handles ambiguous typeof queries.
  • Adds regression coverage and baselines.
File Description
tsc/​internal/​parser/​parser.go Updates type-argument parsing and ambiguity handling.
tsc/​testdata/​tests/​cases/​compiler/​parseGenericArrowRatherThanLeftShift2.tsx Adds parser regression cases.
tsc/​testdata/​baselines/​reference/​compiler/​parseGenericArrowRatherThanLeftShift2.types Records inferred types.
tsc/​testdata/​baselines/​reference/​compiler/​parseGenericArrowRatherThanLeftShift2.symbols Records resolved symbols.
tsc/​testdata/​baselines/​reference/​compiler/​parseGenericArrowRatherThanLeftShift2.js Records emitted JavaScript.

🧠 Review effort: Balanced


func (p *Parser) parseTypeArguments() *ast.NodeList {
if p.token == ast.KindLessThanToken {
if p.reScanLessThanToken() == ast.KindLessThanToken {
@ahmad65saad

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

This branch has not been deployed

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

Labels

For Backlog Bug PRs that fix a backlog bug

Projects

Status: Not started

Development

Successfully merging this pull request may close these issues.

Bad parsing behaviour for generic arrow type arguments in class heritage / JSX open element

2 participants