为何ts-morph的getParentWhileKind无法返回预期的父CallExpression?
使用ts-morph分析代码时,想从Identifier(foo)的位置获取其父级CallExpression,调用.getParentWhileKind(SyntaxKind.CallExpression)却返回null——明明存在对应的父级CallExpression,这是为什么?忽略了什么?除了链式调用.getParent().getParent()...之外,还有什么解决办法?
代码示例
import { Identifier, Project, SyntaxKind } from "ts-morph"; console.clear(); const project = new Project(); const sourceFile = project.createSourceFile( "test.ts", ` const fn = () => { chain.foo.bar('arg'); } ` ); const a = sourceFile.getDescendants().find((d) => d.getText() === "foo"); console.log({ a: a?.getParentWhileKind(SyntaxKind.CallExpression) });
原因分析
你误解了getParentWhileKind的工作逻辑:这个方法会从直接父节点开始向上遍历,仅当当前父节点匹配指定语法类型时才会继续往上走,一旦遇到不匹配的节点就立即终止遍历并返回null。
在你的代码AST结构中,foo的父链是:Identifier(foo) → PropertyAccessExpression(chain.foo) → PropertyAccessExpression(chain.foo.bar) → CallExpression(chain.foo.bar('arg'))
调用getParentWhileKind(SyntaxKind.CallExpression)时,第一个父节点是PropertyAccessExpression,和目标类型不匹配,方法直接终止遍历,所以返回null。
解决办法
方法1:用getAncestors筛选(推荐)
getAncestors会返回当前节点的所有祖先节点数组,直接筛选出类型为CallExpression的节点即可:
const callExpr = a?.getAncestors().find(ancestor => ancestor.getKind() === SyntaxKind.CallExpression);
方法2:手动循环遍历父节点
通过循环逐个向上查找,直到找到目标类型节点:
let currentNode = a; let targetCallExpr = null; while (currentNode) { if (currentNode.getKind() === SyntaxKind.CallExpression) { targetCallExpr = currentNode; break; } currentNode = currentNode.getParent(); }
方法3:用getParentUntil反向匹配
getParentUntil会遍历父节点直到遇到符合条件的节点,返回该节点:
const callExpr = a?.getParentUntil(node => node.getKind() === SyntaxKind.CallExpression);
内容的提问来源于stack exchange,提问作者Jon Sud

