tsconfig noEmit 与 noEmitOnError 配置详解
noEmit
作用:不生成输出文件,但文件报错会有提示信息。
noEmitOnError
作用:报错时不生成输出文件。
在执行 tsc 进行编译时,如果 noEmitOnError 配置为 true 时,如果编译的某个文件中有 ts 报错, 则 ts 将不会产生输出文件。
两者的关键差别
它们的名字很像,作用却是两个维度的:
noEmit: true是永远不产出,不管有没有报错。tsc退化成一个纯类型检查器。noEmitOnError: true是有条件产出,没报错就正常写文件,有报错就一个都不写。
noEmitOnError 默认是 false,这一点常常出人意料——TypeScript 默认是「报错也照样产出 JS」的。因为类型错误大多不影响生成的 JS(类型注解本来就要被擦掉),tsc 的设计哲学是尽量让你拿到可运行的产物。所以 CI 里如果只跑 tsc 就发布,类型错误是拦不住的,必须显式开 noEmitOnError,或者把类型检查和构建拆成两步。
noEmitOnError 的行为是全局的、原子的:只要项目里任何一个文件有错,所有输出文件都不写(增量编译时已存在的旧文件也不会被更新)。不是「只跳过出错的那个文件」。
noEmit 的典型用法
现在绝大多数前端项目的 JS 产物是 esbuild / SWC / Babel 生成的,它们只做语法转换、直接丢弃类型,速度快但完全不做类型检查。所以 tsc 的职责就只剩类型检查了:
{
"compilerOptions": {
"noEmit": true,
"strict": true
}
}
{
"scripts": {
"typecheck": "tsc --noEmit",
"build": "vue-tsc --noEmit && vite build"
}
}
Vite 官方模板里的 vue-tsc --noEmit && vite build 就是这个思路:先让 tsc(vue-tsc 是它的 Vue SFC 增强版)跑一遍类型检查,通过了再让 Vite 打包。这样类型错误会中断构建,同时打包仍然享受 esbuild 的速度。
命令行 tsc --noEmit 和配置文件里写 "noEmit": true 等价,命令行参数优先级更高。项目里已经配了 noEmit: true 时再加命令行参数是冗余的,但也无害。
只想产出类型声明
noEmit: true 会连 .d.ts 一起不产出,这对发布 npm 包是个问题——包需要类型声明。这时组合是:
{
"compilerOptions": {
"declaration": true,
"emitDeclarationOnly": true,
"outDir": "dist/types"
}
}
emitDeclarationOnly 是「只产出 .d.ts,不产出 .js」,正好填上 noEmit 和完整编译之间的空档。注意它要求 declaration 或 composite 至少开一个,否则 tsc 会报 Option 'emitDeclarationOnly' cannot be specified without 'declaration' or 'composite'。
另外 noEmit 和 emitDeclarationOnly 同时为 true 会直接报冲突错误,二者互斥。
noEmit 和 isolatedModules / project references 的坑
noEmit: true 时 --build 模式和 project references 会出问题。因为 project references 依赖被引用项目产出的 .d.ts 和 .tsbuildinfo 来做增量判断,noEmit 把这些都掐掉了,tsc --build 会报 Referenced project may not disable emit。
解决办法是给被引用的子项目单独一份不带 noEmit 的配置,或者用 TypeScript 5.6 引入的 noCheck 反过来做——只产出不检查:
{
"compilerOptions": {
"noCheck": true
}
}
noCheck 和 noEmit 恰好是一对镜像。把两者拆到不同配置文件里,就能实现「检查」和「产出」两条完全独立、可并行的流水线,大仓库里能省不少时间。
相关的几个 emit 控制项
{
"compilerOptions": {
// 不产出 sourcemap 之外的注释
"removeComments": true,
// 不产出 helper 函数(__extends 等),改为从 tslib 导入
"importHelpers": true,
// 不产出被 declare 标记的文件对应的 js(默认行为,一般不用管)
"noEmitHelpers": false,
// 输出目录,不设则 js 和 ts 放在一起
"outDir": "./dist",
// 保留 ts 文件的目录结构基准
"rootDir": "./src"
}
}
noEmitHelpers 和 importHelpers 容易搞混:前者是「不生成 helper,也不导入」,需要你自己保证全局有这些函数;后者是「不生成 helper,改成从 tslib 导入」。发布库时用 importHelpers 配 tslib 依赖,能避免每个文件都内联一份相同的 helper 代码。
编辑器里的报错和 tsc 的报错可能不一致
这是个实际会遇到的困扰:VS Code 里干干净净,跑 tsc --noEmit 却一堆错。原因通常是编辑器只检查当前打开的文件及其依赖,而 tsc 检查 include / files 匹配到的全部文件。没打开过的文件里的错误,编辑器不会主动报。
VS Code 里可以用 TypeScript: Restart TS Server 强制重建,或者装个能跑全项目检查的插件。但最可靠的做法还是在 pre-commit 钩子里跑一次 tsc --noEmit,把这层保证放在流程里而不是靠编辑器。

还没有留言,来发表第一条吧~