TS 编译原理,提供的 API
文章目录
- TS 编译
- 例子
- 错误的例子
- 正确的例子
- 带上 tsconfig.json 的例子
- package.json 中的 tsx 又是什么?
- TS 编译步骤
- 编译目的(两大目的)
- TS AST 相关内容
- TS 提供的 AST 分析 API
- (1)语法级 API(Syntax-level API)
- (2)类型级 API
TS 编译
TypeScript(TS)是一种编译时语言。
- 这和解释型语言(如 JS)有什么区别?
JS 是解释执行语言,代码写完后可以直接执行,浏览器/Node.js 直接运行它。但是TS 需要先编译为 JS,才可以执行。
例子
参考
安装方法 https://ts.xcatliu.com/introduction/get-typescript.html
安装 ts
npm install -g typescript
检查是否安装
tsc --version
错误的例子
一个带类型错误的 hello.ts,用 tsc hello.ts 编译时就会看到类型检查报错。
// hello.ts
function greet(name: string): string {return `Hello, ${name}!`;
}const user: number = "Alice"; // 这里类型错了,user 应该是 number,但赋值了字符串console.log(greet(user)); // 这里传入了错误类型,会报错
tsc hello.ts
当我执行这个命令之后,会生成 js 文件,但同时也会报错。
- 为什么带类型错误的 hello.ts 运行 tsc hello.ts 还会生成 .js 文件?
默认情况下,从 TypeScript 4.0 开始,tsc 编译时的默认行为是:即使有类型错误,也会生成 .js 文件。除非你显式开启 noEmitOnError 选项,或者使用命令行参数 --noEmitOnError
tsc hello.ts --noEmitOnError
或者在 tsconfig.json 里面配置。
正确的例子
// hello.ts
function greet(name: string): string {return `Hello, ${name}!`;
}const user = "Alice";
console.log(greet(user));
tsc hello.ts
当我执行这个命令之后,hello.ts 文件的 同级目录会出现 hello.js。
这直接证明了ts编译的目的(后文有),1 类型检查 2 编译成 js
- 为什么ts不像js一样可执行呢?
- 执行环境只支持纯 JavaScript:浏览器和 Node.js 本身只执行 JS,不管 TypeScript。所以必须先用 tsc 编译器把 TS 转成 JS,才能运行。
- 类型检查是开发时的功能,不存在运行时。TS 的类型检查是在编译阶段完成的,运行时不会有任何类型信息。
带上 tsconfig.json 的例子
// hello.ts
function greet(name: string): string {return `Hello, ${name}!`;
}const user = "Alice";
console.log(greet(user));
根目录自己创建一个 tsconfig.json
{"compilerOptions": {"target": "ES6", // 编译成 ES6 兼容的 JS"module": "commonjs", // 使用 CommonJS 模块系统(Node.js 常用)"strict": true, // 开启严格类型检查"noEmitOnError": true, // 有错误时不生成 JS 文件"outDir": "./dist", // 输出目录,生成的 JS 文件放这里"rootDir": "./", // 源码根目录"esModuleInterop": true // 兼容 CommonJS 和 ESModule 互操作},"include": ["hello.ts"] // 只编译 hello.ts 文件
}
在终端直接执行 tsc,没有类型报错,生成 js
package.json 中的 tsx 又是什么?
- 什么是 tsx?
- tsx 不是 TypeScript 官方自带的命令。
- 它是一个第三方的工具(类似于 ts-node),用来直接运行 TypeScript 文件而无需事先编译成 .js。
假设我有 hello.ts 和 tsconfig.json,我怎么用 package.json 的 tsx 运行 hello.ts 呢?
最开始是这样:
第一步:npm install -D tsx
第二步:修改 package.json,加 “scripts” 字段,告诉 npm 怎么用 tsx 来运行 TS 文件。
{"devDependencies": {"tsx": "^4.20.3"},"scripts": {"start": "tsx hello.ts"}
}
默认情况下,tsx 会自动读取你项目根目录的 tsconfig.json 配置文件,并按照配置执行编译和运行。
第三步:根目录执行 npm run start
- 这就是 tsx 的作用和优势:
- 无需手动编译成 .js,节省步骤,直接运行 TS 文件。
- 运行时内存即时编译,控制台输出就是 TS 代码里 console.log 的内容。
- 同时支持读取 tsconfig.json 里的配置(默认会读),保证类型和路径等配置生效。
TS 编译步骤
参考
- https://juejin.cn/post/6872617267323109389#heading-21
- https://jkchao.github.io/typescript-book-chinese/compiler/overview.html#%E6%96%87%E4%BB%B6%EF%BC%9Autilities
- js 的编译分为三步:词法单元(token流)、抽象语法树(AST)、转换成可执行代码
-
【扫描器】
功能:把源码字符串转换成一系列的 词法单元(Token)。
过程:读取源代码中的字符,识别出关键字、标识符、运算符、字面量(数字、字符串)等基本元素。
结果:得到一串 Token 流,为后续解析做准备。 -
【解析器】
功能:把扫描阶段得到的 Token 流转成抽象语法树(AST)。
过程:根据语言语法规则,将 Token 组织成有层次的结构,形成节点树(AST)。
结果:语法树(AST),描述代码的语法结构,比如函数声明、变量声明、表达式等。 -
【绑定器】
功能:在 AST 上创建符号表(Symbol Table),建立标识符和声明之间的关系。
过程:遍历 AST,找到所有声明(变量、函数、类、接口等),把它们添加到符号表中。
作用域分析:确定每个标识符所属的作用域(全局、函数、块级作用域)。
标识符绑定:标识符和声明建立关联,后面检查阶段能根据符号表找到对应声明。
结果:生成符号表,方便后续类型检查和引用解析。 -
【检查器】
功能:静态类型检查,判断代码是否符合 TS 类型规则。
过程:基于符号表和 AST,推断变量类型,验证函数参数类型、返回值类型,检查接口实现、泛型等是否正确。
类型推断:对于没有显式声明类型的变量,推断其类型。
错误报告:发现类型不匹配、调用错误等,输出编译错误信息。
结果:类型安全保证,确认代码在运行时更少类型错误。
编译目的(两大目的)
类型检查
编译成 js 代码
TS AST 相关内容
https://ts-ast-viewer.com/ 查看 AST 节点内容
- ts 中 每一个AST节点包括
TS 提供的 AST 分析 API
TS 的编译器 API 可以分成两类:(1)语法级 API (2)类型级 API
参考:
- https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API
- GPT
(1)语法级 API(Syntax-level API)
直接操作 AST 节点(不涉及类型系统)
ts.forEachChild(node, callback) – 遍历 AST 子节点
node.getText(sourceFile) – 获取该节点的源码文本
node.pos / node.end – 源码字符范围(基于字符索引)
node.parent – 父节点
node.getSourceFile() – 所属的 SourceFile
(2)类型级 API
依赖于 TypeChecker(即 checker)获取类型信息
https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API
program.getTypeChecker() – 获取类型检查器
checker.getTypeAtLocation(node) – 获取节点的类型对象
checker.getSignaturesOfType(type, ts.SignatureKind.Call) – 获取函数签名