likes
comments
collection
share

TypeScript Compiler API 使用简介

作者站长头像
站长
· 阅读数 62

TypeScript 本身提供了一些 Compiler API,可以生成 TypeScript 代码对应的 AST 等操作。作为官方提供的编译工具,也可以说是目前唯一可用的工具了。TypeScript 官方有一个介绍 Compiler API 的 Wiki,但这个文档内容较少,还很晦涩,这里补充一些基本概念和使用例子。

基本概念

之前写过一篇文章,讲解了TypeScript的编译流程以及一些内部类,其中介绍了一些 TypeScript 中涉及到的类。这里再来简单说一下:

ts.SourceFile

SourceFile 代表一个源文件,可以把它理解为是 AST 的根节点。AST 的节点有一个基类:ts.Node ,SourceFile 继承了这个类。

ts.SyntaxKind

每个 AST 的节点都会有一个 kind 属性,对应 ts.SyntaxKind 类型,用来标记当前节点的种类。所有的种类可以通过ts.SyntaxKind 的定义来查看。

这里面有一个问题,就是 SyntaxKind 种类虽然有名称来进行释义,但是很多时候根据名称并不能确定对应的是哪种语法。

这个时候除了搜索 SyntaxKind 对应的关键词,就只能是根据猜测来尝试了。这里推荐TypeScript AST Viewer,这个工具可以在输入 TypeScript 代码后,实时看到对应的 AST 树。

创建一个 Program

TypeScript 在编译过程中会进行类型检查,而类型信息是需要结合多个文件得出的。例如对 A 文件进行类型检查时,需要知道 A 引入的 B 文件中的变量是什么类型。TypeScript 使用 Program 类来表示一个编译单元,一个 Program 是一些 SourceFile 的集合。

const program = ts.createProgram(fileNames, options);

fileNames 表示一些输入文件,TypeScript 会自动解析这些文件中的 import 等,把 fileNames 对应文件的引用文件也都引入进来。

从 Program 中获取 SourceFile

program.getSourceFiles();

这里举个例子,左边我们有一个 index.ts 文件,这个文件引入了 ./test.ts 文件。右边是我们使用 ts.createProgram 的过程,可以看到我们只传入了 index.ts 。

TypeScript Compiler API 使用简介

把所有 SourceFile 打印出来,可以看到 Program 中引入了大量的文件,其中大部分都是 .d.ts 结尾的类型定义文件。

TypeScript Compiler API 使用简介

此外也包括了 index.ts 用到的 test.ts 文件。

使用 TypeChecker 来获取 AST 节点类型

当我们创建一个 Program 后可以使用 TypeChecker 检查代码中变量的类型信息。

let checker = program.getTypeChecker();

TypeChecker 可以回答以下问题:

  • 这个 Node 对应的 Symbol 是什么?
  • 这个 Symbol 的类型是什么?
  • 在这部分 AST 中,哪些 Symbol 是可见的?
  • 这个文件有哪些 Error ?

TypeChecker 实例的使用很简单,它提供了大量的方法,例如:

const symbol = typeChecker.getSymbolAtLocation(node.name);

const nodeType = typeChecker.getTypeAtLocation(node);

解析 TypeScript 代码,创建 AST

如果我们想要根据 TypeScript 代码创建 AST,修改 AST 后再得到 TypeScript 代码,不进行类型检查。那么就不需要创建 Program,直接创建一个 SourceFile。使用 ts.createSourceFile 方法可以创建一个 SourceFile。

function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes?: boolean, scriptKind?: ScriptKind): SourceFile;

其中将 setParentNodes 设置为 true,那么每个节点会有父节点的信息,比较有用。

修改 TypeScript AST 代码

ts 中提供了一系列的 create 和 update 方法,来进行 AST 的创建和修改。

TypeScript Compiler API 使用简介

TypeScript Compiler API 使用简介

这些方法使用起来有些难度:

  1. ts.Syntax 中定义的类型不是一一对应的,在使用时需要根据名词来进行猜测。
  2. 跟 createProgram 等方法混在了一起,都在 ts 命名空间下。
  3. 没有找到什么文档。

好在都是有类型的,可以根据类型定义来传递需要的参数:

TypeScript Compiler API 使用简介

根据 AST 产出 TypeScript 代码

可以使用 printer 来根据 AST 产出 TypeScript 代码,注意不是产出 JavaScript 代码。

const printer = ts.createPrinter();
printer.printNode(ts.EmitHint.SourceFile, sourceFile, sourceFile);

第二个参数可以制定为 sourceFile 中的某个节点,可以直接产出这个节点的 TypeScript 代码。

printer.printNode(ts.EmitHint.SourceFile, sourceFile.statements[0].expression, sourceFile)

但是第二个参数不能传递任意节点,只能传递这些类型的节点:

enum EmitHint {
    SourceFile = 0,
    Expression = 1,
    IdentifierName = 2,
    MappedTypeParameter = 3,
    Unspecified = 4,
    EmbeddedStatement = 5,
    JsxAttributeValue = 6
}

注意 ts.SourceFile 还有一个 sourceFile.getFullText() 方法, 这个方法只是把创建 sourceFile 时对应的 TypeScript 代码返回,并不会返回修改 AST 之后对应的代码。

简单的使用方法就是这样了,今天就先到这里,后续再补充更多细节。

转载自:https://juejin.cn/post/6844904177286512653
评论
请登录