《TypeScript编程入门》附录 C 常用工具、库与资源

本附录按类别汇总 TypeScript 生态里的常用工具与库:编译器与运行时、打包构建、类型检查与 lint、测试、运行时校验、后端框架、ORM、前端框架,每一类都用「工具 / 用途 / 什么时候选它」三列表格呈现,并给出组合建议与学习资源类型清单。

本附录目标:把 TypeScript 生态里真正会被反复用到的工具按类别列成表,只讲「它是干什么的」与「什么情况下该选它」,不绑定任何具体版本,也不替你决定技术栈——选型永远取决于团队与场景。

附录 C 常用工具、库与资源

TypeScript 生态的一个特点是:官方只提供编译器和语言服务,其余全部由社区承担。这既是活力所在,也是新手最容易迷路的地方——同一个问题往往有五六种解法。

本附录只列类别与用途,不列具体版本号,也不给出任何外部链接。版本与 API 请以你所用工具自身随包发布的文档为准。表格里的「什么时候选它」是经验判断,不是硬性规则。

编译器与运行时

工具用途什么时候选它
tsc官方编译器,同时负责类型检查与产出 JS需要严格类型检查、生成 .d.ts、构建库时首选
ts-node直接运行 .ts,无需预先编译本地脚本、Node 环境的开发调试;生产构建不建议依赖
tsx基于 esbuild 的 TS 运行器需要启动快、支持 ESM 的本地开发与一次性脚本
esbuild极快的打包与转译器,用 Go 编写只做转译不做类型检查,适合开发服务器与构建流水线
swc用 Rust 编写的转译器大型项目里替换 Babel 做语法降级,速度优先
bun内置 TS 支持的运行时与包管理器新项目想「开箱即用跑 TS」,且能接受运行时替换
node --experimental-strip-typesNode 自带的类型擦除能力只想在 Node 里直接跑 .ts,不想引入额外工具
deno内置 TS、安全沙箱优先的运行时需要权限模型与内置工具链的一体化体验

关键认知:esbuild、swc、bun 只做转译,不做类型检查。它们的速度优势正来自「跳过类型」。所以典型组合是「构建用快工具,类型检查用 tsc --noEmit 单独跑一遍」。

// package.json 中常见的双轨脚本
{
  "scripts": {
    "build": "esbuild src/index.ts --bundle --outdir=dist",
    "typecheck": "tsc --noEmit"
  }
}

打包与构建

工具用途什么时候选它
tsup以 esbuild 为内核的库打包器发布 npm 包,需要同时产出 ESM/CJS 与 .d.ts
vite开发服务器 + 构建工具前端项目、需要 HMR 与快速冷启动
rollup以 tree-shaking 著称的打包器构建库、产物需要极致精简
webpack生态最庞大的打包器存量项目、需要大量 loader 与插件
parcel零配置打包器原型或小项目,不想写任何构建配置
unbuild基于 rollup 的库构建工具需要更细的产物控制(多入口、stub 模式)

选型经验:应用选 vite,库选 tsup 或 rollup,存量项目别轻易迁移 webpack。迁移成本往往远高于收益,除非你确实卡在启动速度上。

本书第 16 章 esbuild/swc/tsup 与打包产物 详细讲了转译与类型检查的分工,以及为什么「构建产物里没有类型」。

类型检查与 lint

工具用途什么时候选它
tsc --noEmit纯类型检查,不产出文件所有项目的 CI 必跑项,与构建解耦
typescript-eslint基于 ESLint 的 TS 规则集需要细粒度规则、生态插件丰富的老牌方案
biome集 lint 与格式化于一体的工具新项目想用一个工具搞定 lint + format,配置简单

重要区分:类型检查(tsc)与 lint 是两件事。tsc 回答「类型对不对」,lint 回答「代码风格与潜在缺陷」。二者不能互相替代,CI 里应各跑一次。开启 strict 之后仍有一批规则(如浮空 Promise、未使用变量)需要 lint 兜底。

严格模式的配置细节见 16.1 编译目标与严格模式配置 。

编辑器与语言服务

工具用途什么时候选它
tsserver官方语言服务,提供补全、跳转、重构任何编辑器接入 TS 能力的底座,通常无需手动启动
typescript-language-server基于 tsserver 的 LSP 封装非 VS Code 编辑器(如 Neovim、Emacs)接入 TS
@typescript/vfs在内存里构建虚拟文件系统供编译器使用写类型检查工具、在线演练场、代码生成器
typescript API以编程方式使用编译器写 codemod、自定义 lint 规则、AST 分析脚本

编辑器体验的绝大多数问题都出在「编辑器用的 TS 版本与项目不一致」上。先确认版本,再谈配置。语言服务的能力边界(哪些重构能做、哪些不能)由编译器决定,与编辑器无关。

包管理与工作区

工具用途什么时候选它
npmNode 自带包管理器追求零额外依赖、团队无需额外约定
pnpm内容寻址存储 + 严格依赖隔离Monorepo、依赖臃肿的项目;能显著省盘并暴露幽灵依赖
yarn包管理器,含工作区支持存量项目沿用,或需要特定插件体系
turborepo任务编排与增量构建缓存Monorepo 里希望「只重建受影响包」
changesets版本与变更日志管理需要按变更自动决定语义化版本号并生成发布说明

Monorepo 的典型难点不是「如何装依赖」,而是「如何只检查/构建受影响的包」。这需要包之间的依赖图,而依赖图的正确性又依赖 tsconfig 的 references 配置。相关做法见 16.3 Monorepo 与 Project References 。

文档与类型来源

类型来源说明什么时候用
包自带 .d.ts作者随包发布,最权威优先选择;package.json 的 types 字段指向它
@types/* 社区包DefinitelyTyped 维护,版本独立于原包原包无类型时使用,注意版本可能滞后
自写 .d.ts本地声明补丁前两者都缺时使用,可随项目一起演进
tsc --declaration 产出由源码自动生成自己发布的库应产出,消费方无需手写

一个容易被忽略的事实:@types/* 的版本号与原包不同步。原包升级后 @types 可能仍停留在旧 API,这类不一致往往表现为「运行时正常、类型报错」。遇到时先核对两者版本,再决定是升级 @types 还是自己打补丁。

测试

工具用途什么时候选它
vitest与 Vite 同源的测试框架Vite 项目首选,原生 TS/ESM 支持,启动快
jest老牌测试框架存量项目、已有大量 jest 配置与插件
tsd类型层面的断言库校验 .d.ts 对外暴露的类型是否符合预期
expect-type轻量的类型断言工具想在普通测试文件里顺手写类型断言
msw在测试中拦截网络请求需要稳定的 API 桩,避免测试依赖真实后端
playwright端到端浏览器测试需要覆盖真实渲染与交互路径

运行测试验证「行为」,类型测试验证「类型」。发布库时两者都需要:前者防止逻辑回归,后者防止类型回归——类型回归尤其隐蔽,因为它在运行时毫无表现。

类型测试的具体写法见 15.2 类型测试(tsd/expect-type) 。

运行时校验

工具用途什么时候选它
zod声明式 schema,从 schema 推导静态类型需要在 API 边界做校验并复用同一份契约
valibot函数式、可摇树优化的校验库对包体积敏感(如边缘运行时、前端首屏)
arktype用类型语法直接写校验规则希望校验规则读起来就是 TS 类型
ajv基于 JSON Schema 的校验器契约本身是 JSON Schema(如 OpenAPI 生成物)
typebox用 TS 生成 JSON Schema需要在运行时校验与 JSON Schema 之间共用一份定义

这一类的存在理由只有一个:类型在运行时被擦除,任何来自网络、文件、localStorage 的数据在类型层面都是「声称」而非「事实」。在系统边界处校验一次,内部就能放心使用静态类型。

Zod 的推导机制见 13.2 Zod 模式验证与类型推导 。

后端框架

工具用途什么时候选它
fastify高性能、schema 驱动的 HTTP 框架需要稳定成熟、插件生态完善的传统服务
hono轻量、跨运行时的 Web 框架需要同时跑在 Node、边缘函数、Bun 等多家运行时
nestjs带依赖注入与模块化的企业级框架团队规模较大、需要强约定与分层架构
express最简 HTTP 框架存量项目,或只需要一层极薄的路由
trpc端到端类型安全的 RPC前后端同仓、希望「改后端类型前端立刻报错」

三个框架代表三种哲学:fastify 重性能与插件、hono 重可移植与轻量、nestjs 重架构与约定。选型时先问「我的部署环境是什么」,再问「团队需要多少约束」。

ORM 与数据访问

工具用途什么时候选它
prismaschema 文件驱动的 ORM,自动生成类型安全客户端想要开箱即用的类型安全查询与迁移工具链
drizzle以 TS 写 schema 的轻量 ORM偏好 SQL 手感、希望 schema 就是代码、产物更薄
kysely类型安全的查询构建器想保留手写 SQL 的结构,同时获得列名与结果类型检查
typeorm装饰器风格的 ORM存量项目沿用,或偏好实体类写法

两者的共同点是把数据库表结构变成 TypeScript 类型,让「字段改名漏改一处」这类错误在编译期暴露。差别在于 prisma 把 schema 放在独立文件并用代码生成,drizzle 把 schema 直接写成 TS 代码。

前端

工具用途什么时候选它
react组件库生态最大、招人最容易、需要大量现成组件
vue组件库模板语法上手快、团队有 Vue 背景
next.jsReact 全栈框架需要 SSR/SSG、路由与后端一体化
tanstack query服务端状态管理需要缓存、重试、失效等异步数据编排能力
svelte / solid编译期优化的组件库想减少运行时开销,且团队愿意接受新范式
tanstack router类型安全的路由需要「路由参数与路径都有类型」的强约束

前端的类型收益集中在两处:组件 props 与 API 响应。前者靠框架自身的泛型支持,后者靠前后端共享类型。共享类型的做法见 17.2 前后端共享类型与 API 契约 。

迁移与渐进采用

工具用途什么时候选它
allowJs / checkJs允许并检查 .js 文件从 JS 项目渐进迁移的第一步
// @ts-check单文件开启检查只想先给关键文件加类型,不改构建
ts-migrate批量迁移辅助工具大型 JS 代码库需要一次性铺开类型
tsc --init生成初始配置新项目起步,或想对照默认选项

渐进迁移的顺序建议是:先让编译器参与(allowJs + checkJs),再逐文件加 // @ts-check,最后重命名 .js 为 .ts 并收紧 strict。反过来做(一上来全开 strict)几乎必然失败,因为错误数量会淹没团队。完整方法论见 18.1 JavaScript 项目渐进式迁移 。

学习资源类型

资源按「用途」分四类,比按「形式」分更有用:

资源类型解决什么问题什么时候用
官方手册与参考查语法的权威定义与行为边界需要确认某个写法是否合法、语义如何
类型挑战类练习把类型体操练成肌肉记忆已掌握基础,想突破高级类型与类型性能
社区规范与风格指南统一团队写法,避免无谓争论团队协作前制定约定,或接入 lint 预设
开源项目源码学习真实工程如何组织类型想找「可运行的范例」而非玩具示例

使用建议:官方手册当字典查,类型挑战当习题做,社区规范当约束用,开源源码当范本读。四者角色不同,不要拿其中一种替代另一种——尤其是不要只靠碎片化文章学类型系统。

本书也提供了成体系的自学路径:18.3 学习路径与生态选型 。

组合建议

把上面的表拼起来,三条常见技术栈组合如下:

场景组合理由
前端应用vite + vitest + typescript-eslint + zod开发快、测试同源、边界校验统一
Node 后端tsx(开发)+ tsc --noEmit(CI)+ fastify + prisma类型检查与构建解耦,数据层类型安全
发布 npm 库tsup + tsc --noEmit + vitest + tsd双格式产物、类型与行为双重回归
Monorepopnpm + turborepo + Project References只构建受影响包,依赖图由 tsconfig 保证

无论选哪条,CI 里都必须有一条独立的 tsc --noEmit。这是整个工具链里唯一不可省略的一环:其他工具都可以替换,只有它真正做类型检查。

把选型拆成三个问题,答案会更清晰:

问题影响的选择
部署到哪(Node / 边缘 / 浏览器)运行时、框架、是否能用 Node 专有 API
产物给谁用(应用 / 库)打包器、是否需要产出 .d.ts、是否需要多格式
团队需要多少约束是否上 nestjs 这类强约定框架、lint 规则的严格程度

先回答第一个问题,因为它决定的上限最多;第三个问题最容易引发争论,但通常影响最小。

三个常见反模式

选型阶段有三个反复出现的错误,值得单独列出:

反模式表现后果
用转译器代替类型检查只跑 esbuild / swc,CI 里没有 tsc --noEmit类型错误一路漏到生产,类型系统形同虚设
为了「类型安全」引入全站校验每个内部函数入口都写一遍 schema 校验运行时代价翻倍,收益却集中在边界那一处
追新工具链而忽略团队每季度换一次打包器与 lint 方案配置知识无法沉淀,出问题无人能修

判断某个工具是否值得引入,只需问一句:它替代的那件事,现在真的在疼吗? 如果答案是否定的,那就是在给未来的自己增加维护面。

一份最小可用的工具清单

如果不想在一开始就做复杂选型,下面这套组合足以支撑绝大多数中小型项目,之后按需替换即可:

  • 类型检查:tsc --noEmit,接入 CI,作为唯一的类型真相来源。
  • 开发运行:tsx 或 bun,直接执行 .ts 文件,省去构建步骤。
  • 构建产物:tsup(库)或 vite(应用),转译快、配置少。
  • 测试:vitest,与 TypeScript 的集成开箱即用。
  • Lint:typescript-eslint,其中带类型信息的规则能发现普通 lint 找不到的问题。
  • 运行时校验:zod,只用在接口边界与配置读取处。

这套组合的共同点是「每一件工具都只负责一件事」,替换其中任意一件都不需要动其他部分。等到某个环节真的成为瓶颈,再针对它做替换,比一开始就搭一套复杂流水线要稳妥得多。

延伸阅读

本站另有几篇偏工程实践的文章,可作为本附录的补充:

小结

  • TypeScript 生态只有编译器与语言服务是官方的,其余全部由社区提供,因此「同一问题多种解法」是常态而非异常。
  • tsc 负责类型检查,esbuild / swc / bun 负责快速转译,二者职责不同、必须并存,CI 中独立的 tsc --noEmit 不可省略。
  • 类型检查与 lint 是两件事,前者管类型正确性,后者管代码风格与潜在缺陷,应各跑一次。
  • 运行时校验库(zod 等)存在的唯一理由是「类型在运行时被擦除」,只应在系统边界使用,不必全站铺开。
  • 后端框架、ORM、前端框架的选择本质是「团队与部署环境」的选择,没有普适最优解;先确定部署环境,再确定约束强度。
  • 学习资源要按用途分工:官方手册当字典、类型挑战当习题、社区规范当约束、开源源码当范本。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「typescript」更多文章

  1. 《TypeScript高级编程》11.3 类型驱动架构与团队规范
  2. 《TypeScript高级编程》11.2 渐进式迁移与严格化路径
  3. 《TypeScript高级编程》11.1 TS 版本演进与 breaking changes