首页 / TypeScript 入门教程 / 构建工具集成

TypeScript 入门教程

构建工具集成

本教程共 80 篇 · 第 79 篇 · 更新于 2026-08-10 · 约 13 分钟阅读

TypeScriptTypeScript 入门教程ViteWebpack构建工具esbuildts-loaderbabel

本节目标:搞懂 TypeScript 在现代前端构建流程里到底扮演什么角色——它不是一个”独立的构建工具”,而是一套类型检查管线。学完你会配置 Vite 项目里的 TS 双检查流程,理解 Webpack 的两种 TS 集成路线,以及为什么”类型检查与转译分离”是主流理念。

TypeScript 在构建流程里的位置

先搞清楚一个容易混淆的概念:TypeScript 编译器(tsc)能做两件事——转译(把 TS 编译成 JS)和类型检查。但现代前端工具链里,这两件事通常是分开的。

为什么分开?因为转译可以用更快的工具做(esbuild、SWC、Babel),而类型检查只能靠 tsc。一个 Vite 项目里典型的流程长这样:

你的 .ts 源码

     ├──→ esbuild(Vite 内置)→ 剥离类型、转译成 JS → Vite 打包 → 浏览器
     │        只做转译、不做类型检查、超快

     └──→ tsc --noEmit → 只做类型检查、不产出 JS 文件
              在 CI 或编辑器中运行

也就是说 TypeScript 在你的项目里充当的是”静态分析器”的角色——它告诉你能不能通过类型检查,但不负责把代码跑起来。

“你的浏览器里没有 TypeScript”——这句话值得记住。浏览器只认识 JS/HTML/CSS。所有的类型标注、泛型、接口最终都会被构建工具剥掉,变成纯 JS。构建工具帮你完成了从”开发环境有类型”到”运行环境只有 JS”的翻译工作。

Vite:内置 TS 支持,但只做转译

Vite 从第一天起就内置了 TypeScript 支持。你在 Vite 项目里写 .ts / .tsx 文件,不需要额外装任何插件。

但它的 TS 支持有一个边界:**Vite 用 esbuild 做转译,不做类型检查。**这意味着:

  • 你的 .ts 文件会被快速转成 .js,开发服务器秒启动
  • 类型错误不会阻塞开发服务器——就算你有 10 个类型错误,Vite 照样把页面跑起来
  • 类型检查需要一个独立的步骤,由 tsc 来完成

这就是 esbuild 的”只管转不管查”策略。esbuild 在转译时直接把类型标注删掉——它不检查类型是否匹配:

// 你写的代码
function greet(name: string): string {
  return `Hello, ${name}!`;
}

// esbuild 转译后(近似)
function greet(name) {
  return `Hello, ${name}!`;
}

快的原因:esbuild 是用 Go 写的原生二进制,不需要加载 Node.js 的庞大类型系统。

Vite 项目的标准 TS 配置

创建一个 Vite + TS 项目的标准方式:

npm create vite@latest my-app -- --template vanilla-ts

默认生成的 tsconfig.json 核心配置:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "jsx": "react-jsx",
    "skipLibCheck": true,
    "noEmit": true,
    "isolatedModules": true
  },
  "include": ["src"]
}

两个关键选项:

  • noEmit: true:tsc 不产出 JS 文件。因为 Vite 的 esbuild 已经在做转译了,用不着 tsc 再输出一份。
  • isolatedModules: true:开启单文件转译模式。这个选项要求你的 TS 代码在不依赖全局类型的前提下能被正确转译——跟 esbuild 的”逐文件转译”行为对齐。

双管线:转译管线 + 类型检查管线

Vite 项目推荐在 package.json 里配两条独立的 script:

{
  "scripts": {
    "dev": "vite",
    "build": "tsc -b && vite build",
    "typecheck": "tsc --noEmit",
    "preview": "vite preview"
  }
}
  • dev:开发模式。Vite 负责转译 + 热更新,不做类型检查。如果你需要在开发时即时看到类型错误,依赖编辑器的 TypeScript 语言服务(VS Code 默认就有)。
  • build:生产构建。先跑 tsc -b(完整类型检查 + 构建模式),通过了再让 Vite 打包。类型错误在这里会阻塞构建——在生产环境里不让有类型问题的代码上线。
  • typecheck:纯类型检查命令。在 CI 里单独跑,或者在提交前跑一遍。
Tip

tsc -b(构建模式)比 tsc --noEmit 更适合 CI。前者利用增量构建缓存(.tsbuildinfo),第二次跑会快很多。TypeScript 7.0 的增量构建在 Go 编译器下更加高效。

编辑器层面的类型检查

VS Code 的 TypeScript 扩展会在你保存文件的时候实时做类型检查。所以开发模式下(vite dev),类型检查并没有消失——只是换了一个地方运行:

场景谁做类型检查阻塞开发?
写代码时VS Code 的 TS 语言服务不阻塞,红色波浪线提醒
开发服务器不做
生产构建tsc -b阻塞
CI / pre-committsc --noEmit阻塞

这种分层设计是当前前端工程化的共识——开发体验要快、发布流程要严格。

Webpack:两条路线,两种哲学

Webpack 的 TypeScript 集成比 Vite 老得多,也复杂得多。历史上主要有两条路线。

路线 A:ts-loader

ts-loader 是 TypeScript 官方维护的 Webpack loader。它直接在 Webpack 构建流程里调用 tsc:

npm install -D ts-loader typescript
// webpack.config.js
module.exports = {
  entry: "./src/index.ts",
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: "ts-loader",
        exclude: /node_modules/,
      },
    ],
  },
  resolve: {
    extensions: [".tsx", ".ts", ".js"],
  },
};

ts-loader 的优势是完整——它调用真正的 tsc,所以能做完整的类型检查。TypeScript 7.0 下 ts-loader 本身没问题,但注意:

TypeScript 7.0 没有暴露稳定的编译器 API。如果你的 ts-loader 配置使用了 transpileOnly: true(跳过类型检查仅转译),可以继续用。如果用了完整的类型检查模式(依赖编译器 API),这需要等 TypeScript 7.1 的新 API 或者继续用 @typescript/typescript6

// ts-loader 的 transpileOnly 模式
{
  test: /\.tsx?$/,
  use: {
    loader: "ts-loader",
    options: {
      transpileOnly: true, // 只转译,不做类型检查
    },
  },
}

transpileOnly: true 模式下 ts-loader 不会做类型检查,你可以用 fork-ts-checker-webpack-plugin 额外起一个进程单独做类型检查——这就是”转译与检查分离”理念在 Webpack 里的体现。

路线 B:Babel + @babel/preset-typescript

另一条路线是完全不用 tsc 做转译,改用 Babel:

npm install -D @babel/core @babel/preset-env @babel/preset-typescript babel-loader
// webpack.config.js
module.exports = {
  entry: "./src/index.ts",
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: [
              "@babel/preset-env",
              "@babel/preset-typescript",
            ],
          },
        },
      },
    ],
  },
  resolve: {
    extensions: [".tsx", ".ts", ".js"],
  },
};

@babel/preset-typescript 做的是和 esbuild 一样的事:**把类型标注删掉,不检查类型是否正确。**它的优势在于:

  • Babel 生态丰富——如果你项目里已经用了很多 Babel 插件(polyfill、JSX 转换等),加一个 preset 就能支持 TS
  • 转译速度比 ts-loader 快(不调用 tsc 的完整管线)
  • 类型检查完全由 tsc 单独负责,职责分离

两条路线对比

ts-loaderBabel + preset-typescript
转译原理调用 tsc 的完整编译管线Babel 逐文件删除类型语法
类型检查可内置(有性能代价)必须单独跑 tsc
速度默认模式较慢,transpileOnly 较快
生态依赖 TypeScript 编译器融入 Babel 生态
适合场景想要”一站式”方案的小项目已有 Babel 配置的项目
TS 7.0 兼容性transpileOnly 可继续;完整模式待 7.1 API完全兼容

目前的推荐:如果新项目用 Webpack,Babel + preset-typescript + tsc --noEmit 是最干净的方案。ts-loader 的 transpileOnly 模式也是一个不错的选择。

Note

除了 ts-loader 和 Babel,还有 esbuild-loaderswc-loader 这两种更快的选择。它们都遵循”只转译不检查”的模式,本质哲学一致。

类型检查与转译分离:为什么这是对的

回到本章开头抛出的那个问题——为什么要把类型检查和转译分开?

原因有三:

1. 速度

转译工具(esbuild、SWC、Babel)可以并行处理文件,因为它们不需要”理解”跨文件的类型关系。但类型检查必须构建一个完整的类型关系图——A 函数返回的类型要跟 B 函数的参数类型对上、interface 的继承链要解析完。这个过程天然是”全局”的。

把”快的事情”和”慢的事情”拆开,快的不会等慢的。

2. 职责清晰

构建工具的职责是”让我看到我的代码跑起来的样子”,类型检查器的职责是”告诉我代码有没有类型错误”。这两个目标在开发阶段是独立的——你改了一个按钮的颜色,你不需要跑一遍完整的类型检查来验证。

3. 工具替换灵活

如果转译和检查耦合在一起,你想换个更快的转译工具就得重新处理类型检查的集成。分离之后,你可以今天用 esbuild、明天换 SWC,类型检查那边完全不受影响。

这套理念在 TypeScript 7.0 时代只会更强化——tsc 变成了一个更快的 Go 原生二进制,但它仍然是一个”独立进程”,通过 LSP 协议和编辑器/构建工具通信。

TypeScript 7.0 在构建工具方面的变化

TypeScript 7.0 对构建工具的影响主要在两点:

并行检查器的引入

TypeScript 7.0 的实验性并行类型检查器(--checkers 标志)让你在做 tsc --noEmit 类型检查的时候能利用多核 CPU。对于 CI 里的类型检查步骤,这能显著缩短时间:

# TypeScript 7.0,用 8 个 worker 并行检查
npx tsc --noEmit --checkers 8

默认是 4 个 worker。具体该设多少取决于你的 CI 机器的 CPU 核心数——设太高反而会有调度开销。建议从默认值开始,根据实际情况调整。

框架项目的兼容性

Vite 的 TS 支持不受影响——它用的是 esbuild 做转译,和 TypeScript 编译器没有直接 API 依赖。

Webpack 的 Babel + preset-typescript 路线也不受影响

受影响的是依赖 TypeScript 编译器 API 的工具:ts-loader 的完整模式、一些自定义的 Webpack plugin、以及 Angular/Vue/Svelte 框架中的编辑器语言服务。这些工具在 TypeScript 7.1 发布新 API 之前,需要继续使用 TypeScript 6.x 的 API(通过 @typescript/typescript6 包)。

详细的框架项目升级建议见第 80 章 “TypeScript 7.0 迁移指南”。

回顾全书:从类型到工程

这是本书倒数第二章。走到这里,你已经:

  • 掌握了 TypeScript 的类型系统(第 8–53 章)
  • 学会了把 JS 项目迁移到 TS 的策略(第 78 章)
  • 理解了构建工具如何与 TypeScript 协作(本章)

你不再是一个”在 Playground 里写类型体操”的初学者了。你理解 TypeScript 在生产环境里是怎么跑起来的——它跟 Vite 怎么配合、跟 Webpack 怎么集成、为什么 CI 里要跑 tsc --noEmit

下一章(第 80 章)是本书的最后一章——一个实际的 TypeScript 7.0 迁移指南。从 5.x/6.x 升到 7.0,你需要改什么、注意什么、做完之后能获得什么收益。把那一章读完,你就可以自信地把团队项目升级到 TypeScript 7.0 了。