Skip to content

Commit 9acd475

Browse files
committed
Fix getChildren() dropping opening '<' token when immediately followed by '<' (#64168)
In addSyntheticNodes (packages/typescript/src/ast/astnav.ts), re-scan LessThanLessThanToken when tokenEnd exceeds the gap end. This splits '<<' into '<' (LessThanToken) so getChildren() preserves opening angle brackets on type argument lists immediately following '<'.
1 parent 6a19dd0 commit 9acd475

2 files changed

Lines changed: 7 additions & 2 deletions

File tree

‎packages/typescript/src/ast/astnav.ts‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -709,8 +709,12 @@ function addSyntheticNodes(children: Node[], pos: number, end: number, parent: N
709709
scanner.resetTokenState(pos);
710710
scanner.scan();
711711
while (pos < end) {
712-
const token = scanner.getToken();
713-
const tokenEnd = scanner.getTokenEnd();
712+
let token = scanner.getToken();
713+
let tokenEnd = scanner.getTokenEnd();
714+
if (tokenEnd > end && token === SyntaxKind.LessThanLessThanToken) {
715+
token = scanner.reScanLessThanToken();
716+
tokenEnd = scanner.getTokenEnd();
717+
}
714718
if (tokenEnd <= end) {
715719
// An identifier should never appear as trivia between AST children; skip defensively.
716720
if (token !== SyntaxKind.Identifier) {

‎packages/typescript/test/sync/ast.test.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1319,6 +1319,7 @@ describe("RemoteNode + child/token getters", () => {
13191319
{ 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 },
13201320
{ name: "JSX component with reparsed @param props type", source: '/**\n * @param {{ name: string }} props\n * @returns {object}\n */\nexport function Greeting(props) {\n return <div className="greeting">hello {props.name}<br /></div>;\n}\n', js: true, jsx: true },
13211321
{ 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 = <span title={item.label}>{item.label}</span>;\n', js: true, jsx: true },
1322+
{ name: "type argument lists with contiguous opening angles", source: "type Bar = ReturnType<<T>(x: T) => number>; const foo = bar<<T>(x: T) => number>();" },
13221323
];
13231324

13241325
for (const entry of corpus) {

0 commit comments

Comments
 (0)