如何正确合并两个从字符串创建的Typescript AST实例
TypeScript 双AST合并问题解决方案
报错根因
- 报错
"Unhandled SyntaxKind: SourceFile.":直接将完整的ts.SourceFile类型的ast2作为替换节点传入转换器,TypeScript 打印机无法识别嵌套在普通语法节点内的源文件节点,因此触发异常。 - 手动提取箭头函数后模板字面量/JSX失效:两个核心原因,一是创建源文件时未开启JSX解析配置,导致JSX和内嵌模板字面量解析异常;二是从其他源文件中提取的节点保留了原始位置、父节点绑定信息,直接复用会导致打印机输出错误代码。
修正步骤
1. 调整AST创建方法,支持JSX解析
你的template2包含JSX语法,需要指定脚本类型为TSX才能正确解析:
const createAST = (str: string, isJsx = false) => ts.createSourceFile( isJsx ? "file.tsx" : "file.ts", // 文件名后缀匹配语法类型 str, ts.ScriptTarget.Latest, true, // 显式指定脚本类型,JSX内容需要用TSX模式解析 isJsx ? ts.ScriptKind.TSX : ts.ScriptKind.TS )
2. 提取目标节点,避免直接使用SourceFile
从ast2中剥离外层的SourceFile、ExpressionStatement包装,拿到真正需要替换的箭头函数节点:
const extractArrowFunc = (sourceFile: ts.SourceFile): ts.ArrowFunction | null => { for (const stmt of sourceFile.statements) { if (ts.isExpressionStatement(stmt) && ts.isArrowFunction(stmt.expression)) { return stmt.expression } } return null }
3. 重置节点元信息,避免打印异常
从其他AST提取的节点需要克隆并清除原始位置信息,打印机遇到位置标记为-1的节点会重新生成正确的代码片段:
const replaceTransformer = (targetNode: ts.Node): ts.TransformerFactory<ts.SourceFile> => { return context => { // 深度克隆目标节点,避免污染原始AST const clonedNode = ts.factory.cloneNode(targetNode, true) // 清除原始位置信息 clonedNode.pos = -1 clonedNode.end = -1 const visit: ts.Visitor = node => { if (ts.isArrowFunction(node)) return clonedNode return ts.visitEachChild(node, visit, context) } return node => ts.visitNode(node, visit) } }
4. 调整调用逻辑
const template1 = `const arr = fields.map(() => {})` const template2 = `(field) => <Toolbar id={\`\${field.id}.\${field.name}\`} />` const ast1 = createAST(template1) const ast2 = createAST(template2, true) const targetArrowFunc = extractArrowFunc(ast2) if (!ast1 || !targetArrowFunc) { console.log("节点解析失败") return } const result = ts.transform(ast1, [replaceTransformer(targetArrowFunc)]) const printer = ts.createPrinter() const outputCode = printer.printFile(result.transformed[0]) console.log(outputCode)
输出结果
运行后会得到正确的合并代码:
const arr = fields.map((field) => <Toolbar id={`${field.id}.${field.name}`} />)
内容的提问来源于stack exchange,提问作者Martin Mecir
相关产品推荐
相关产品推荐

