isolatedModules 和 verbatimModuleSyntax 分别解决什么问题?
简化版
isolatedModules 要求每个文件都能被单独转译,适合 Babel、SWC、esbuild 这类按文件编译的工具链。它会禁止一些必须依赖跨文件类型分析才能安全转译的写法。
verbatimModuleSyntax 更强调保留你写的 import/export 语义,类型导入需要明确写 import type,减少编译器自动删改模块语句带来的歧义。
详细版
isolatedModules 常见影响:
namespace Foo {
export const x = 1;
}
某些全局 namespace、const enum 等写法在单文件转译时会有问题。
verbatimModuleSyntax 鼓励:
import type { User } from "./types";
import { createUser } from "./service";
类型导入和值导入分开,构建工具更容易判断运行时依赖。
完整版教学
一、现代前端构建常不是 tsc 转译
很多项目用 TypeScript 做类型检查,但转译交给 Babel、SWC、esbuild 或 Vite。
这些工具通常按单文件处理,不做完整类型分析。
这两个选项都和“类型系统信息如何影响最终 JavaScript 模块输出”有关。
二、isolatedModules 的核心约束
它要求每个文件单独看也能安全转译。
file.ts -> transformer -> file.js
如果某个语法必须知道其他文件的类型信息才能决定输出,就不适合单文件转译。
三、常见受影响写法
例如只导出类型但文件不是模块,或者使用某些 namespace/const enum 场景,都可能触发限制。
export type User = {
id: string;
};
让文件显式成为模块,可以避免一些全局脚本歧义。
export {};
四、verbatimModuleSyntax 关注模块语句保真
过去 TypeScript 可能根据使用情况删除只用于类型的 import。
verbatimModuleSyntax 倾向于:你写什么模块语句,就按语义保留或要求你明确标注。
import type { User } from "./types";
import { api } from "./api";
这样运行时依赖和类型依赖更清楚。
五、为什么 import type 很重要
import type { User } from "./types";
表示这个导入只用于类型,运行时不需要加载模块值。
| 写法 | 运行时含义 |
|---|---|
import type | 类型导入,可擦除 |
import | 值导入,可能保留运行时依赖 |
export type | 类型导出 |
这对包体积、副作用模块和循环依赖都有影响。
六、工程实践怎么选
Vite、Next.js、现代组件库等项目通常会开启或接近这些约束。
建议:
- 类型和值导入分开
- 避免依赖 namespace 和 const enum 黑魔法
- 使用
tsc --noEmit做类型检查 - 让打包工具负责转译
- 库项目关注输出模块格式
如果项目老代码很多,开启前要评估迁移成本。
七、常见误区与追问
- 误区:isolatedModules 会让类型检查更严格到运行时。 它主要约束单文件转译安全性。
- 误区:import type 只是代码风格。 它能明确类型依赖,影响模块输出和副作用判断。
- 误区:verbatimModuleSyntax 会自动修好所有导入。 它更像要求你把语义写清楚。
- 误区:tsc 不 emit 时这些选项没意义。 它们能提前暴露和真实转译工具不兼容的代码。
- 追问:为什么 Babel/SWC 需要 isolatedModules? 它们通常没有完整 TS 类型图,只能单文件转译。
- 追问:副作用模块为什么要注意导入擦除? 错误擦除可能导致副作用代码没有执行。
八、加强记忆
记忆方式:
isolatedModules -> each file can compile alone
verbatimModuleSyntax -> imports mean what they say
import type -> type-only dependency
回答时把它们放到现代构建链里解释:tsc 查类型,Babel/SWC/esbuild 转译,模块语句必须清楚,单文件转译必须安全。