diff --git a/packages/typescript/src/ast/astnav.ts b/packages/typescript/src/ast/astnav.ts index f401215034b9d..e9457ceb5fef9 100644 --- a/packages/typescript/src/ast/astnav.ts +++ b/packages/typescript/src/ast/astnav.ts @@ -709,8 +709,12 @@ function addSyntheticNodes(children: Node[], pos: number, end: number, parent: N scanner.resetTokenState(pos); scanner.scan(); while (pos < end) { - const token = scanner.getToken(); - const tokenEnd = scanner.getTokenEnd(); + let token = scanner.getToken(); + let tokenEnd = scanner.getTokenEnd(); + if (tokenEnd > end && token === SyntaxKind.LessThanLessThanToken) { + token = scanner.reScanLessThanToken(); + tokenEnd = scanner.getTokenEnd(); + } if (tokenEnd <= end) { // An identifier should never appear as trivia between AST children; skip defensively. if (token !== SyntaxKind.Identifier) { diff --git a/packages/typescript/test/sync/ast.test.ts b/packages/typescript/test/sync/ast.test.ts index 1783da4ac3185..3e27448d66d2f 100644 --- a/packages/typescript/test/sync/ast.test.ts +++ b/packages/typescript/test/sync/ast.test.ts @@ -1319,6 +1319,7 @@ describe("RemoteNode + child/token getters", () => { { name: "JS with reparsed types on exported and nested functions", source: "/** @param {number} a */\nexport function outer(a) {\n /** @returns {number} */\n function inner() { return a; }\n return inner();\n}\n", js: true }, { name: "JSX component with reparsed @param props type", source: '/**\n * @param {{ name: string }} props\n * @returns {object}\n */\nexport function Greeting(props) {\n return
hello {props.name}
;\n}\n', js: true, jsx: true }, { name: "JSX with @type cast and @typedef around elements", source: '/**\n * @typedef {Object} Item\n * @property {string} label\n */\nconst item = /** @type {Item} */ ({ label: "x" });\nconst el = {item.label};\n', js: true, jsx: true }, + { name: "type argument lists with contiguous opening angles", source: "type Bar = ReturnType<(x: T) => number>; const foo = bar<(x: T) => number>();" }, ]; for (const entry of corpus) {