Skip to content

编译流程与配置

核心问题解析

问题一:.ts 文件怎么变成 .js?

TypeScript 并不是直接运行的,它需要经过一个“翻译”过程:

text
example.ts → [tsc 编译] → example.js

这个过程由 TypeScript Compiler(tsc) 完成,主要分为三个阶段:

  • 解析(Parsing)

    将 .ts 源码字符串解析成 AST(抽象语法树),识别出变量、函数、类型等结构。

  • 绑定(Binding)

    构建符号表,建立变量、类型、作用域之间的引用关系,形成语义网络。

  • 类型检查(Checking)

    遍历 AST,根据类型规则验证类型安全性(如:string 能否赋给 number)。

  • 代码生成(Emitting)

    在类型检查通过后,擦除类型信息,生成纯净的 JavaScript 代码。

类型是“编译时”的存在,运行时完全消失。

示例:

ts
// math.ts
function add(a: number, b: number): number {
return a + b;
}

编译后生成:

js
// math.js
function add(a, b) {
return a + b;
}

答案:.ts → AST → 类型检查 → 擦除类型 → .js

问题二:tsconfig.json 到底控制了什么?

tsconfig.json 是 TypeScript 项目的配置中枢,它告诉 tsc:

  • 从哪开始编译?
  • 编译到哪里?
  • 用什么语法标准?
  • 是否开启严格检查?

一个典型的 tsconfig.json

text
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"declaration": true
},
"include": ["src"]
}

答案:tsconfig.json 控制了编译行为、输出路径、语言标准、类型检查严格度等所有关键环节。

问题三:为什么有些错误不报,有些却报了?

你可能遇到这种情况:

ts
let x = 10;
x = "hello"; // 有时报错,有时不报?

这取决于 tsconfig.json 中的配置,尤其是:

  • noImplicitAny: 是否允许隐式 any
  • strictNullChecks: 是否严格检查 null/undefined
  • strict: 是否开启所有严格模式
  • 如果 strict: false,TS 会放宽很多检查,导致“看似错误”的代码也能通过。

答案:配置项决定了类型检查的严格程度。TS 不是“不报错”,而是你“没开检查”。

学习目标详解

目标一:理解编译阶段的三个核心步骤

阶段作用输出
1. 解析(Parsing)将源码转为 AST抽象语法树
2. 绑定(Binding)建立符号与作用域关系符号表、语义图
3. 检查(Checking)验证类型安全性错误/警告信息
4. 生成(Emitting)擦除类型,生成 JS.js、.d.ts、.js.map

关键点

  • 类型检查发生在生成代码之前
  • 即使有错误,默认也会生成 .js(除非 noEmitOnError: true)

目标二:掌握 tsconfig.json 关键配置项

配置项作用推荐值
target编译后的 JS 版本"ES2020" 或 "ESNext"
module模块系统"ESNext"(现代项目)
outDir编译输出目录"./dist"
rootDir源码目录"./src"
strict开启所有严格检查true ✅
declaration生成 .d.ts 类型声明文件true(库项目必备)
sourceMap生成 source maptrue(调试用)
esModuleInterop兼容 CommonJS 导入true

最佳实践

  • 项目初始化时使用 tsc --init 生成默认配置
  • 始终开启 "strict": true
  • 明确设置 outDir 和 rootDir 避免输出混乱
  • 目标三:理解 --watch 与增量编译机制
  • 开发时不想每次手动运行 tsc,可以使用:
bash

tsc --watch

它会:

  • 监听文件变化
  • 只重新编译修改的文件
  • 利用编译缓存实现增量编译(Incremental Compilation)

大幅提升开发效率,接近“实时编译”。

原理

  • tsc 会记录文件依赖图
  • 修改一个文件后,只重新编译它及其依赖者
  • 可通过 --incremental 启用磁盘缓存(.tsbuildinfo 文件)

目标四:熟悉 lib 和 types 配置对全局类型的控制

lib: 指定内置 API 的类型定义

text

{
"compilerOptions": {
"lib": ["ES2020", "DOM", "DOM.Iterable"]
}
}
  • 不写 lib:TS 默认根据 target 自动引入(如 ES5 + DOM)
  • 手动指定:精确控制可用的全局对象(如 Promise、console、document)

错误示例:target: ES5 但未引入 ES6 的 lib,则 Promise 会报错。

types: 指定自动引入的类型包

text
{
"compilerOptions": {
"types": ["node", "@types/react"]
}
}
  • 控制哪些 @types/* 包会被自动加载
  • 默认会加载 node_modules/@types 下的所有包(可能导致命名冲突)
  • 显式指定可减少污染

总结:本节核心要点

概念关键点
编译流程解析 → 绑定 → 检查 → 生成(类型被擦除)
tsconfig.json项目配置的核心,控制一切编译行
strict必开!确保类型安全
--watch开发利器,支持增量编译
lib控制可用的全局 API(如 Promise, Array.from)
types控制自动引入的第三方类型

下一节我们将进入第二阶段,学习如何用接口、泛型等构建类型安全的代码结构。

练习题

练习题 1:补全 tsconfig.json

补全以下 tsconfig.json,要求:

  • 源码在 ./src
  • 输出到 ./dist
  • 使用 ES2021 语法
  • 开启严格模式
  • 生成类型声明文件
text

{
"compilerOptions": {
"target": "______",
"module": "ESNext",
"outDir": "______",
"rootDir": "______",
"strict": ______,
"declaration": ______
},
"include": ["______"]
}

练习题 2:判断编译结果

有以下代码:

ts
// src/utils.ts
export const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));

如果 tsconfig.json 中 "target": "ES5","lib": ["ES5"],是否会报错?为什么?如何修复?

练习题 3:理解 --watch 机制

tsc --watch 修改文件后立即重新编译,它是否每次都全量编译?

它是如何提升编译效率的?请简述其背后机制。

练习题 4:lib 配置的影响

以下配置中,哪些全局对象可用?哪些不可用?

text
{
"compilerOptions": {
"lib": ["ES5"]
}
}

选项:Array.from、Promise、console.log、document.getElementById

练习题 5:类型检查开关

假设你有以下代码:

ts

function log(value) {
console.log(value.len);
}

在什么配置下这段代码能通过编译?在什么配置下会报错?

如何强制它报错?