Typescript 编译过程

本文详细介绍了Typescript的编译过程,包括预处理器如何处理待编译文件,语法分析器生成AST,联合器联合声明,类型解析器与检查器进行类型检查,以及生成器如何产出JavaScript和声明文件。重点探讨了编译上下文的构建,AST节点,Symbol和TypeChecker的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Typescript 编译器核心

  • 语法分析器(Parser):从一系列原文件开始, 根据语言的语法, 生成抽象语法树(AST)
  • 联合器(Binder):使用一个Symbol将相同结构的声明联合在一起(例如:同一个接口或模块的不同声明,或拥有相同名字的函数和模块)。这能帮助类型系统推导出这些具名的声明
  • 类型解析器与检查器(Type resolver / Checker):解析每种类型的构造,检查读写语义并生成适当的诊断信息
  • 生成器(Emitter):从输入文件(.ts和.d.ts)生成输出结果,结果可以是以下形式之一:JavaScript(.js),声明(.d.ts),或者是source maps(.js.map)
  • 预处理器(Pre-processor):“编译上下文”指的是某个“程序(program)”里涉及到的所有文件。上下文的是通过检查所有从命令行上传入编译器的文件,按顺序,然后再加入这些文件的直接或间接引用的文件(import 和 /// <reference path=... />引入的文件)创建的。根据引用图,你会发现它是一个有序的源文件列表,他们组成了整个程序(program)。当解析 import 导入的的时候,会优先选择 .ts 文件而不是 .d.ts 文件,以确保处理的是最新的文件。编译器选择根据模块解析策略进行导入模块的解析。导入解析失败不会报error,因为可能已经声明了外部模块

二、数据结构

  • Node : AST 的基本构建单元块。通常来说,Node 代表了语法中的非终端节点。与非终端节点相对的终端节点,比如标识符、字面量等,也在 AST 中
  • SourceFile :对应源文件的 AST 。SourceFile 本身是一个 Node,它额外提供了一些接口,用于访问包括原始文本、文件包含的引用、标识符列表,以及字符位置映射
  • Program :编译单元的所有 SourceFile 和编译选项的集合。它是类型系统和代码生成的主要入口
  • Symbol:已命名的声明,由类型联合器所生成。它连接了 AST 中的声明节点和其他地方的同名声明实体。它是语义系统的基本构建单元块
  • Type: 它是语义系统的另一部分,它可以是具名的(如类、接口),也可以是匿名的(如对象字面量)
  • Signature: TS 语言中包含三种类型签名:函数调用签名、构造函数签名和索引签名

三、编译过程详解

3.1、预处理器(preprocessing)处理

预处理器负责根据待编译文件计算参与编译的文件,生成源文件列表,构成编译上下文 和 Program

3.1.1、待编译文件

默认为项目目录下所有的 .ts、.tsx、.d.ts 为待编译文件

注意:
1. 默认 node_modules 是通过 exclude 排除在外的
2. 如果指定 allowJs 为 true 则 .js 和 .jsx 也会包含在内
3. 影响待编译文件配置有:files、 include、 exclude 

3.1.2、需计算的文件

根据待编译文件中如下方式引入的文件:

  1. /// <reference path=... /> 标签引入的依赖声明文件
  2. import 表达式引入的文件

注意:
当解析 import 导入的的时候,会优先选择 .ts/.tsx文件而不是 .d.ts 文件,以确保处理的是最新的文件

3.1.3、默认包含的文件

所有可见的 @types 目录下的所有文件

如:node_modules/@types./node_modules/@types/等等

3.2、语法分析器(parser)处理

语法分析器将预处理器得到的源文件列表中的文件解析生成包含抽象语法树(AST)Node 的 SourceFile 对象

SourceFile对象 = 源文件 AST + 额外信息 (如文件名及文件信息等)

3.3、联合器(Binder)处理

联合器遍历并处理语法分析器生成的 AST,并将 AST 中的声明结合放到一个 Symbol 中。

一个 Symbol 会对应到一个命名实体。 这里有个一微妙的差别,几个声明节点可能会是名字相同的实体。 也就是说,有时候不同的Node具有相同的Symbol,并且每个Symbol保持跟踪它的声明节点。 比如,一个名字相同的class和namespace可以合并,并且拥有相同的Symbol。 联合器也会处理作用域,以确保每个Symbol都在正确的封闭作用域里创建

然后通过 createSourceFile API 生成带有 Symbol的 SourceFile

SourceFile对象 = 源文件 AST + Symbol + 额外信息 (如文件名及文件信息等)

此时的 Symobl 仅表示单个文件的声明信息

3.4、类型解析器与检查器(Type resolver / Checker)处理

3.4.1、生成 Program

通过调用 createProgramAPI 来创建 Program

Program = All SourceFile + CompilerOptions

3.4.2、生成 TypeChecker 进行处理

通过 Program 实例创建 TypeChecker

TypeChecker是TypeScript类型系统的核心,它负责计算出不同文件里的Symbols之间的关系,将Type赋值给Symbol,并生成任何语义Diagnostic(比如:error)

处理内容:

  1. TypeChecker 合并不同的 SourceFile 里的 Symbol 到一个单独的视图,创建单一的Symbol表(囊括所有文件的全局Symbol视图 )
  2. 类型检查

Symbol 合并到一张表后,TypeChecker就可以解决关于这个程序的任何问题了。 这些“问题”可以是:
1. 这个Node的Symbol是什么?
2. 这个Symbol的Type是什么?
3. 在AST的某个部分里有哪些Symbol是可见的?
4. 某个函数声明的Signature都有哪些?
5. 针对某个文件应该报哪些错误?
TypeChecker计算所有东西都是“懒惰的”;为了回答一个问题它仅“解决”必要的信息。 TypeChecker仅会检测和这个问题有关的Node,Symbol或Type,不会检测额外的实体。

3.5、生成器(Emitter)处理

通过 Program 创建一个 Emitter

Emitter 将给定的 SourceFile 生成编译后文件(.js.jsx.d.ts.js.map

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值