- 文档
- 教程
【免费下载链接】typescript-book
The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.
2026 年 7 月 24 日,TypeScript 原生代码库(Go 实现)合并了一组面向工具链的编程式输出生成 API,为需要生成 JavaScript 或声明文件(.d.ts)的构建工具、打包器与语言服务器提供了四条不同的 emit 路径。本文基于 TypeScript 官方新闻与当前仓库源码,系统梳理这四个 API 的行为差异、阻止选项语义、虚拟文件系统支持,以及它们在真实项目(本仓库书籍构建工具链)中的经典用法对照,帮助你为 TypeScript 7 原生 API 时代的工具迁移做好准备。
背景:为什么需要新的 Emit API
TypeScript 7.0 是首个基于 Go 原生代码库的稳定版本,官方公布的基准测试显示其完整构建速度约为 TypeScript 6 的 7.7~11.9 倍,语言服务也迁移到了 Language Server Protocol(详见 typescript-7-released.md)。但原生迁移带来的一个重要代价是:TypeScript 7.0并未提供稳定的编程式 API,当前依赖ts.createProgram()、program.emit()嵌入编译器的工具(包括部分 Astro、Vue、MDX、Svelte 工作流)仍需停留在 TypeScript 6;官方团队在 7.0 发布时便预告新 API 将在后续版本补齐(参见 typescript-7-release-candidate.md)。
本次合并正是这一补齐动作的关键一环:原生Program对象新增了输出生成(emit)能力,为那些"必须真正产出 JavaScript 或声明文件"的工具提供了官方编程入口。
核心变更:四条 Emit 路径
新 API 在原生Program上提供了四个方法,它们按输出目的地(文件系统 vs 内存)和文件选择范围(整个程序 vs 指定文件)两个维度区分:
| 方法 | 输出目的地 | 选择范围 | 是否尊重阻止输出选项(noEmit/noEmitOnError) |
|---|---|---|---|
program.emit(emitOnly?: EmitOnly) | 文件系统(含配置的虚拟文件系统) | 整个程序 | 是 |
program.emitToString(emitOnly?: EmitOnly) | 内存中的字符串结果 | 整个程序 | 是 |
program.getJavaScriptEmit(files?: readonly DocumentIdentifier[]) | 内存中的 JavaScript 输出 | 选定的文件 | 否(绕过) |
program.getDeclarationEmit(files?: readonly DocumentIdentifier[]) | 内存中的声明输出 | 选定的文件 | 否(绕过) |
这四条路径为 API 使用者提供了两类互补能力:常规的整程序输出(写盘或取字符串)与定向的内存输出(按需取某个文件的 JS 或声明结果)。
1.program.emit(emitOnly?: EmitOnly):写盘输出
这是传统语义最接近的方法:将整个程序的输出写入文件系统,包括写入调用方通过宿主(host)配置的虚拟文件系统。它会遵守noEmit与noEmitOnError这类阻止生成的编译选项——也就是说,当项目配置了noEmit: true,或在noEmitOnError: true下存在类型错误时,该方法不会产出任何文件。可选的EmitOnly参数用于进一步限定输出类型(例如仅产出声明文件或仅产出 JavaScript),其具体取值集在源码未稳定前以目标版本的类型声明为准。
2.program.emitToString(emitOnly?: EmitOnly):整程序内存输出
与emit()的语义几乎一致(整个程序、尊重阻止选项),区别仅在于结果以内存字符串形式返回,而不是写盘。它适合需要拿到"完整编译产物文本"但不希望触碰磁盘的场景,例如在内存中打包、生成代码快照或做后续转换。由于同样受noEmit/noEmitOnError约束,它在"允许输出"的配置下才有实际结果。
3.program.getJavaScriptEmit(files?: readonly DocumentIdentifier[]):定向取 JS
与前两个方法不同,此方法绕过阻止输出的选项,即使配置了noEmit也能返回结果——因为它本质上是"我要看这个文件编译成 JS 是什么样",而不是"把项目构建产物写出去"。调用方通过可选的files参数(readonly DocumentIdentifier[])精确指定要取哪些文件的输出;不传文件时则返回全部。返回值是内存中的 JavaScript 输出,适合编辑器内联预览、增量编译、按需转译等场景。
4.program.getDeclarationEmit(files?: readonly DocumentIdentifier[]):定向取声明
与第 3 条路径对称,它返回选定文件的声明文件输出(.d.ts文本),同样绕过阻止选项、同样基于内存。对需要为库项目按需生成类型定义的发布流水线而言,这比整程序跑一遍emit更轻量——可以只针对被外部引用的入口文件产出声明。
参数语义与设计意图
EmitOnly:出现在两个整程序方法上,用于裁剪输出种类。其核心作用是让"只想要声明"或"只想要 JS"的工具免于生成它们不需要的产物。DocumentIdentifier:出现在两个定向方法上,用于标记"要取哪些文件的输出"。具体实现可能是路径、文件 ID 或快照句柄(以目标版本的 API 声明为准),files为可选参数,缺省时覆盖全程序。
两个维度的正交组合(写盘/内存 × 全程序/选文件)正是本次设计的核心:普通整程序输出(第 1、2 条)保持传统emit的配置语义,保证"项目说不能输出就不输出";定向内存输出(第 3、4 条)则服务于读取型工具,即便项目禁止写盘,也可以安全地在内存中读取单个文件的产物。
阻止选项(noEmit/noEmitOnError)的语义边界
新 API 明确区分了两类方法对编译选项的响应,这在实际集成中需要特别注意:
noEmit: true:禁止编译器产出任何输出。整程序方法会因此空手而归;定向方法仍可返回内存结果。noEmitOnError: true:当存在类型检查错误时阻止输出。整程序方法在出错时停止生成;定向方法不受影响。
以本仓库书籍工具链为例,tools/compile.ts 在编译书中所有 TypeScript 代码片段时使用了经典的ts.createProgram()+program.emit()流程,并显式设置noEmitOnError: true、strict: true,随后通过emitResult.emitSkipped判断输出是否被跳过、据此决定进程退出码(emitSkipped为真则process.exit(1))。这段代码直观展示了两点:其一,noEmitOnError这类选项在经典 API 中就深度参与 emit 决策;其二,工具链必须读取 emit 结果状态而不能默认产物一定存在——这正是新 API 中"整程序路径尊重阻止选项、定向路径绕过"设计的一致性延续。
与虚拟文件系统的联动
本次 emit API 合并的另一层意义在于与原生 API 的虚拟文件系统(VFS)能力衔接。原生快照 API 已支持通过createFileSystem、createFileSystemWithLib、createFileSystemLayer构造 VFS 对象,其中full型 VFS 完全驻留内存、不回退到宿主回调,layer型 VFS 在缓存未命中时回退宿主并可用removedPaths隐藏文件(详见 typescript-native-api-adds-layered-vfs.md)。program.emit()明确支持"写入配置的虚拟文件系统",意味着工具可以在不触碰真实磁盘的前提下完成一次完整的构建写盘流程——例如在沙箱或测试环境中执行整程序 emit,再通过 VFS 读取产物校验。可以推断,这为后续"内存中构建 + 内存中取产物"的工具形态提供了底层支撑。
可用性与版本现状
- 该变更于2026 年 7 月 24 日合并到 TypeScript 原生代码库,对应的官方 pull request 编号为 typescript-go#4699。
- 官方新闻明确提醒:源码未指明包含这些 API 的稳定 npm 版本。TypeScript 7.0 本身没有稳定编程 API(参见 typescript-7-released.md),因此工具作者不应假设某个
typescript@latest版本一定具备上述方法。 - 迁移期间,需要稳定编程 API 的工具可以继续使用
@typescript/typescript6兼容包与新版编译器并行运行(参见 typescript-7-release-candidate.md)。 - 原生代码库的长期去向也已明确:
tsgo这一名称将退出,原生代码会回归主 TypeScript 仓库并共享 issue 跟踪(参见 typescript-7-native-tooling-consolidates.md),因此后续 API 演进会在主仓库中持续发生。
实操建议:工具在接入这四条方法前,应在运行期探测program对象上是否存在对应方法(例如typeof program.emitToString === 'function'),再决定走新路径还是回退到经典 API;同时务必按"整程序方法可能受noEmit影响、定向方法不受影响"的语义设计降级逻辑。
小结
TypeScript 7 原生 API 的 emit 方法补齐,标志着原生代码库从"仅检查"走向"可产出"的关键一步。四个方法用两个正交维度(文件系统/内存、全程序/选文件)覆盖了工具链的典型需求:需要完整产物时用emit()或emitToString()(尊重项目输出禁令),需要按文件取 JS 或声明时用getJavaScriptEmit()/getDeclarationEmit()(绕过禁令、纯内存读取)。结合对noEmit/noEmitOnError的差异化处理与虚拟文件系统支持,新 API 为下一代 TypeScript 工具链提供了清晰、可组合的编程接口。
本文新闻条目索引见 typescript-news/index.md,英文原文位于 typescript-7-native-api-adds-emit-methods.md。
- 文档
- 教程
【免费下载链接】typescript-book
The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.
相关推荐
TypeScript 7 原生 API 新增 Emit 输出方法:四条路径覆盖全程序与定向生成
TypeScript 7 原生 API 新增 Emit 输出方法:四条路径覆盖全程序与定向生成 2026 年 7 月 24 日,TypeScript 原生(Go
文档教程TypeScript 7 原生 API 新增 emit 方法:四条程序化输出通道与工具集成实战解析
TypeScript 7 原生 API 新增 emit 方法:四条程序化输出通道与工具集成实战解析 TypeScript 7 的原生代码库(基于 Go 的新编译
文档教程TypeScript 7 原生 API 新增 emit 方法:文件系统与内存输出的四种程序化路径解析
TypeScript 7 原生 API 新增 emit 方法:文件系统与内存输出的四种程序化路径解析 文章导读 :TypeScript 7 的原生代码库(Go
文档教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考