TypeScript:获取语法树

58

我已经阅读了整个互联网,但找不到有关从TypeScript源代码中获取语法树(就像在Esprima中一样)的示例。

我的意思是,我该如何获得像这样的对象(Esprima Parser 示例)。

{
    "type": "Program",
    "body": [
        {
            "type": "VariableDeclaration",
            "declarations": [
                {
                    "type": "VariableDeclarator",
                    "id": {
                        "type": "Identifier",
                        "name": "answer"
                    },
                    "init": {
                        "type": "BinaryExpression",
                        "operator": "*",
                        "left": {
                            "type": "Literal",
                            "value": 6,
                            "raw": "6"
                        },
                        "right": {
                            "type": "Literal",
                            "value": 7,
                            "raw": "7"
                        }
                    }
                }
            ],
            "kind": "var"
        }
    ]
}

来自 JavaScript 代码

var answer = 6 * 7;

仅适用于TypeScript源代码文本?

P.S. 我非常希望得到您的帮助,因为我不想自己写一个可怕的自行车)

P.P.S. 我认为库文件typescript.ts(.js)和typescriptServices.ts(.js)可以帮助我,但是我不知道如何使用 :(

已解决

非常感谢用户Steve Fenton的帮助。如果有人感兴趣,这是我的代码:

// uses
var typeScriptLS =  new Harness.TypeScriptLS();
var ServicesFactory = new Services.TypeScriptServicesFactory();
var serviceShim = ServicesFactory.createLanguageServiceShim(typeScriptLS);

// add lib.d.ts
var _libText = window.document.getElementById('lib.d.ts').innerText;
typeScriptLS.addScript('lib.d.ts', _libText.replace(/\r\n?/g,"\n"), true);

// add greeter.ts
var _sourceText = window.document.getElementById('greeter.ts').innerText;
typeScriptLS.addScript('greeter.ts', _sourceText.replace(/\r\n?/g,"\n"), true);

// script name
var _scriptName = 'greeter.ts';
// get syntax tree
var _st = serviceShim.languageService.getSyntaxTree(_scriptName);
//console.log(_st);
console.log(JSON.stringify(_st, "", 2));

5
在部署的文件中Harness在哪里定义?我在tsc.jstypescript.jstypescriptServices.js中都没有看到" Harness "。更广泛地说,你如何运行这个已解决的代码示例? - Jason Kleban
抱歉让你久等了。/src/harness/ 在 https://github.com/Microsoft/TypeScript 上。但是现在我的代码可能会变得无效。 - bukvaG
2
可能是如何获取TypeScript的语法树?的重复问题。 - ColinE
不要使用Harness,因为它不是公共API,您需要在TypeScript项目内部完成(因此您需要重新分发它),或者访问非公共的TypeScript API(如果用户未使用正确的TypeScript编译器版本,则会破坏您的软件)。可以使用babylon解析器来完成此操作-请参见我的下面的答案。 - cancerbero
4个回答

28

TypeScript解析器并不直接生成像那样的树形结构,但您仍然可以使用它的对象模型来执行各种操作。例如,我们在某些工具中使用它来进行语法转换以进行测试。以下是一个代码片段,您可以使用它来打印语法树:

import ts from "typescript";
 
const code = "enum { x = 1 }";
const sc = ts.createSourceFile("x.ts", code, ts.ScriptTarget.Latest, true);

let indent = 0;
function print(node: ts.Node) {
    console.log(new Array(indent + 1).join(" ") + ts.SyntaxKind[node.kind]);
    indent++;
    ts.forEachChild(node, print);
    indent--;
}
 
print(sc);

我该如何将以下代码:var TypeScript = require('./typescriptServices'); 添加到 app.js 文件中,以便它能够运行? - Cameron Taggart
我在这里更详细地解释了问题:http://stackoverflow.com/questions/23983998/how-do-i-require-the-typescriptservices-d-ts-in-a-node-js-app - Cameron Taggart
2
我已经弄清楚了,并在我的博客http://blog.ctaggart.com/2014/06/typescript-ast-from-nodejs.html上写下了我的发现。 - Cameron Taggart
1
请注意,在nodejs上运行此示例需要var TypeScript = require('typescript-services');而不是var TypeScript = require('typescript');。我花了很长时间才弄清楚这些奇怪的未定义错误来自哪里。但后一个模块没有导出语句。 - yankee

9

这个问题之前在九月就已经提出了。

目前还没有可以为您完成此操作的方法-没有一个神奇的getSyntaxTree方法可供调用来执行此操作。

TypeScript编译器是开源的,完全使用TypeScript编写,因此您可以扫描它以查找是否有可用/添加句柄的内容。

其中的好处是,根据两个问题的赞数,您有很大机会将您的工作作为开源项目发布,因为有一些人需要此功能。

或者,使用EsprimaSpiderMonkey从编译后的JavaScript(实际上将在运行时执行的代码)中获取语法树。


1
解析发出的 JavaScript 会丢失很多信息(所有类型)。我认为这个问题的动机就在于此。 - cancerbero

6
使用recast和babylon@next是可能的。虽然您必须信任这些技术定义的语法来表示TypeScript代码AST,并且它们将保持最新状态——因为TypeScript每次发布新的语言特性(短时间内)——不像其他语言(JavaScript),您有明确定义的版本和标准发布——所以如果您的用户开始使用新的语言特性,这些技术(我猜测是babylon)应该跟上更新,否则解析将失败。
// npm install recast babylon@next
const source = `
interface I {
  color: string
}
class C implements I{
  color: string='blue'
}
`
const recast = require('recast')
const tsParser = require("recast/parsers/typescript")
const ast = recast.parse(source, {
  parser: tsParser
});
console.log(`
CODE: 

${source}

AST: 

${JSON.stringify(ast)}
`);

2
我发现重构的效果非常好。例如:
var recast = require('recast');
var ast = recast.parse(`var answer = 6 * 7;`);
console.log(ast);

这将输出所有需要的信息和事件TypeAnnotation,所以这个库真的很棒 :)
[
   {
      "type": "VariableDeclaration",
      "declarations": [
         {
            "type": "VariableDeclarator",
            "id": {
               "type": "Identifier",
               "name": "answer",
               "typeAnnotation": {
                  "type": "TypeAnnotation",
                  "typeAnnotation": {
                     "type": "NumberTypeAnnotation",
                     "loc": {
                        "start": {
                           "line": 1,
                           "column": 12
                        },
                        "end": {
                           "line": 1,
                           "column": 18
                        },
                        "lines": {},
                        "indent": 0
                     }
                  },
                  "loc": {
                     "start": {
                        "line": 1,
                        "column": 10
                     },
                     "end": {
                        "line": 1,
                        "column": 18
                     },
                     "lines": {},
                     "indent": 0
                  }
               },
               "loc": {
                  "start": {
                     "line": 1,
                     "column": 4
                  },
                  "end": {
                     "line": 1,
                     "column": 18
                  },
                  "lines": {},
                  "indent": 0
               }
            },
            "init": {
               "type": "BinaryExpression",
               "operator": "*",
               "left": {
                  "type": "Literal",
                  "value": 6,
                  "raw": "6",
                  "loc": {
                     "start": {
                        "line": 1,
                        "column": 21
                     },
                     "end": {
                        "line": 1,
                        "column": 22
                     },
                     "lines": {},
                     "indent": 0
                  }
               },
               "right": {
                  "type": "Literal",
                  "value": 7,
                  "raw": "7",
                  "loc": {
                     "start": {
                        "line": 1,
                        "column": 25
                     },
                     "end": {
                        "line": 1,
                        "column": 26
                     },
                     "lines": {},
                     "indent": 0
                  }
               },
               "loc": {
                  "start": {
                     "line": 1,
                     "column": 21
                  },
                  "end": {
                     "line": 1,
                     "column": 26
                  },
                  "lines": {},
                  "indent": 0
               }
            },
            "loc": {
               "start": {
                  "line": 1,
                  "column": 4
               },
               "end": {
                  "line": 1,
                  "column": 26
               },
               "lines": {},
               "indent": 0
            }
         }
      ],
      "kind": "var",
      "loc": {
         "start": {
            "line": 1,
            "column": 0
         },
         "end": {
            "line": 1,
            "column": 27
         },
         "lines": {},
         "indent": 0
      }
   }
]

1
据我尝试,Recast无法识别完整的TypeScript语法。 - Jaime
快要完成了-那是JavaScript,对于任何TypeScript都不起作用,例如:let a: string,但可以使用自定义解析器实现,请参见我上面的答案。 - cancerbero

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接