gpt4 book ai didi

typescript - 如何在使用 Compiler API 进行类型检查之前转换 TypeScript 代码

转载 作者:行者123 更新时间:2023-12-04 11:30:30 26 4
gpt4 key购买 nike

意图
我想使用 TypeScript 的 Compiler API在 TypeScript 代码中试验运算符重载。具体来说,我想找到 x + y 的所有实例并将它们变成 op_add(x, y) .但是,我希望语言服务(例如 VS Code 中的 IntelliSense)了解转换并显示正确的类型。
例如在这段代码中:

interface Vector2 { x: number, y: number }
declare function op_add(x: Vector2, y: Vector2): Vector2
declare let a: Vector2, b: Vector2

let c = a + b
我希望当我将鼠标悬停在 c 上时,它会显示 Vector2 .

计划
为了实现这一目标,我必须:
  • 创建一个公开与 typescript 相同的 API 的程序– 以同样的方式 ttypescript 做。
  • 在将源代码传递给 typescript 之前,让该程序修改源代码
  • 让 VS Code(或任何编辑器)使用我的包而不是 typescript

  • 执行
    我首先创建了一个名为 compile.ts 的简短脚本。使用编译器 API 解析名为 sample.ts 的文件进入 AST .然后直接修改AST,改变 Binary(x, PlusToken, y)Call(op_add, x, y) .最后,它将修改后的代码打印到控制台,然后尝试发出。对于 IDE 集成而言,仅此是不够的,但这是一个好的开始。 compile.ts :
    import * as ts from "typescript"
    import { possibleChildProperties } from "./visit";

    let program = ts.createProgram(['sample.ts'], { target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS })
    let inputFiles = program.getSourceFiles()
    const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed })

    let outputCode: string

    for (let input of inputFiles) {
    if (input.fileName === 'sample.ts') {
    ts.visitNode(input, visitor) // modifies input's AST
    outputCode = printer.printNode(ts.EmitHint.Unspecified, input, input)
    break
    }
    }

    console.log(outputCode) // works
    let emitResult = program.emit() // fails



    function visitor(node: ts.Node): ts.Node {
    if (node.kind === ts.SyntaxKind.BinaryExpression) {
    let expr = node as ts.BinaryExpression

    if (expr.operatorToken.kind === ts.SyntaxKind.PlusToken) {
    return ts.createCall(ts.createIdentifier('op_add'), [], [expr.left, expr.right])
    }
    }

    return visitChildren(node, visitor)
    }

    function visitChildren(node: ts.Node, visitor: ts.Visitor) {
    for (const prop of possibleChildProperties) {
    if (node[prop] !== undefined) {
    if (Array.isArray(node[prop]))
    node[prop] = node[prop].map(visitor)
    else
    node[prop] = visitor(node[prop])
    }
    }

    return node
    }

    sample.ts :
    let a = { a: 4 }
    let b = { b: 3 }
    let c = a + b
    console.log输出:
    let a = { a: 4 };
    let b = { b: 3 };
    let c = op_add(a, b);

    问题
    当打码机工作正常并输出正确的代码时,调用 program.emit()导致未指定的内部错误。这可能意味着我正在以不受支持的方式修改 AST。
    /home/m93a/Dokumenty/tsc-experiments/node_modules/typescript/lib/typescript.js:100920
    throw e;
    ^

    Error: start < 0
    at createTextSpan (/home/m93a/Dokumenty/tsc-experiments/node_modules/typescript/lib/typescript.js:10559:19)
    at Object.createTextSpanFromBounds (/home/m93a/Dokumenty/tsc-experiments/node_modules/typescript/lib/typescript.js:10568:16)
    at getErrorSpanForNode (/home/m93a/Dokumenty/tsc-experiments/node_modules/typescript/lib/typescript.js:13914:19)
    at createDiagnosticForNodeInSourceFile (/home/m93a/Dokumenty/tsc-experiments/node_modules/typescript/lib/typescript.js:13808:20)
    at Object.createDiagnosticForNode (/home/m93a/Dokumenty/tsc-experiments/node_modules/typescript/lib/typescript.js:13799:16)
    at error (/home/m93a/Dokumenty/tsc-experiments/node_modules/typescript/lib/typescript.js:35703:22)
    at resolveNameHelper (/home/m93a/Dokumenty/tsc-experiments/node_modules/typescript/lib/typescript.js:36602:29)
    at resolveName (/home/m93a/Dokumenty/tsc-experiments/node_modules/typescript/lib/typescript.js:36274:20)
    at getResolvedSymbol (/home/m93a/Dokumenty/tsc-experiments/node_modules/typescript/lib/typescript.js:52602:21)
    at checkIdentifier (/home/m93a/Dokumenty/tsc-experiments/node_modules/typescript/lib/typescript.js:54434:26)

    问题
    在运行类型检查器之前修改程序的 AST 的正确方法是什么? 我知道 AST 最好是只读的,但标准 ts.visitEachChild 只能在类型检查后使用。并且自己深度克隆节点似乎也不是一个可行的选择,因为没有任何方法可以创建 Program来自代码生成的 AST。

    更新
    编辑 1 :正如@jdaz 所注意到的,我的 sample.ts缺少 op_add 的声明,这可能会导致问题。我将此行添加到文件的顶部:
    declare function op_add(x: {}, y: {}): string
    现在有一个不同的错误——文件​​诊断的生成失败:
    /home/m93a/Dokumenty/tsc-experiments/node_modules/typescript/lib/typescript.js:100920
    throw e;
    ^

    Error: Debug Failure. Expected -2 >= 0
    at Object.createFileDiagnostic (/home/m93a/Dokumenty/tsc-experiments/node_modules/typescript/lib/typescript.js:17868:18)
    at grammarErrorAtPos (/home/m93a/Dokumenty/tsc-experiments/node_modules/typescript/lib/typescript.js:69444:36)
    at checkGrammarForAtLeastOneTypeArgument (/home/m93a/Dokumenty/tsc-experiments/node_modules/typescript/lib/typescript.js:68771:24)
    at checkGrammarTypeArguments (/home/m93a/Dokumenty/tsc-experiments/node_modules/typescript/lib/typescript.js:68777:17)
    at checkCallExpression (/home/m93a/Dokumenty/tsc-experiments/node_modules/typescript/lib/typescript.js:59255:18)
    at checkExpressionWorker (/home/m93a/Dokumenty/tsc-experiments/node_modules/typescript/lib/typescript.js:61687:28)
    at checkExpression (/home/m93a/Dokumenty/tsc-experiments/node_modules/typescript/lib/typescript.js:61597:38)
    at checkExpressionCached (/home/m93a/Dokumenty/tsc-experiments/node_modules/typescript/lib/typescript.js:61275:38)
    at checkVariableLikeDeclaration (/home/m93a/Dokumenty/tsc-experiments/node_modules/typescript/lib/typescript.js:63983:69)
    at checkVariableDeclaration (/home/m93a/Dokumenty/tsc-experiments/node_modules/typescript/lib/typescript.js:64051:20)

    最佳答案

    这可能是一种hacky方式,但既然你已经有了修改过的源代码,为什么不从中构建一个新的AST呢?例如:

    const newSource = ts.createSourceFile(
    'newSource.ts',
    outputCode,
    ts.ScriptTarget.ES5,
    true
    )
    const newProgram = ts.createProgram(['newSource.ts'], { target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS })
    let emitResult = newProgram.emit()
    这避免了对原始 AST 的更改并且运行时不会出错。

    关于typescript - 如何在使用 Compiler API 进行类型检查之前转换 TypeScript 代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63105982/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com