TypeScript项目实现Tree Shaking的关键是配置"module": "esnext"确保输出ES模块,配合"importsNotUsedAsValues": "remove"清除类型导入,并在package.json中设置"sideEffects": false,使构建工具能静态分析并移除未使用代码。

要在 TypeScript 项目中为 Tree Shaking 提供坚实基础,关键不是“开启某个开关”,而是让编译输出严格匹配构建工具(如 Vite 或 Rollup)对静态分析的要求。核心目标是:生成纯净、无副作用、结构清晰的 ES 模块代码。
必须设为 ES 模块输出
TypeScript 编译器若输出 CommonJS,Tree Shaking 就会彻底失效——因为 require 是动态的,无法在构建时静态判定哪些导出未被使用。
- "module": "esnext"(不可用 "commonjs"、"amd" 或 "umd")
- "target": "es2020" 或更高(避免降级引入冗余 helper 或 polyfill)
- "importsNotUsedAsValues": "remove"(自动移除仅用于类型声明的 import,防止污染模块图)
- 禁用 "outDir" 或 "declaration" 不影响运行时,但若启用了 "composite": true,需确认引用路径未意外触发全量编译
避免类型导入污染模块结构
即使只写了一个 import type,若配置不当,也可能导致整个模块被保留。TypeScript 默认保留所有 import,除非明确告知哪些只是类型用途。
- 启用 "importsNotUsedAsValues": "remove" 后,
import type { Foo } from './types'和import { type Bar } from './utils'都不会出现在 JS 输出中 - 避免混用
import * as X from 'pkg'和export * from 'pkg',这类通配符会阻断静态分析 - 慎用 "esModuleInterop": true 和 "allowSyntheticDefaultImports": true,它们可能插入额外的包装逻辑,干扰摇树判断
配合构建工具对齐模块格式
tsconfig 控制“输入怎么转成 JS”,而 Vite/Rollup 控制“JS 怎么打包”。两者必须一致,否则中间环节会破坏 ESM 结构。
- Vite 默认以 ESM 方式加载 .ts 文件,但若 tsconfig 输出 CJS,Vite 实际处理的是已降级的代码
- Rollup 的
@rollup/plugin-typescript插件需显式设置compilerOptions: { module: 'esnext' },不能依赖默认值 - 检查最终产出的 .js 文件:应含
export const xxx = ...和import { y } from './z',而非const xxx = require(...)或exports.xxx = ...
主动清理无用导出与副作用标记
编译器不负责删代码,它只提供干净的模块结构;真正执行摇树的是构建工具。但你可以通过代码组织帮它做更准的判断。
- 每个文件只导出实际被外部使用的成员,避免
export *或大对象解构导出 - 在 package.json 中设置 "sideEffects": false(若项目纯函数无副作用),或 "sideEffects": ["*.css", "*.scss"](仅标记有副作用的资源)
- 第三方库优先选
lodash-es、date-fns等原生 ESM 版本,避开只有 CJS 的包











