要让工程工作流在编译打包阶段自动将无副作用的函数声明行内展开,核心在于启用构建工具链(如 Terser、esbuild)的函数内联优化,并通过 @__PURE__ 注释或 --pure 参数显式标记无副作用函数,确保其在生产构建时被安全内联。

要让工程工作流在编译打包阶段自动将无副作用的函数声明行内展开,核心在于启用编译器的函数内联(function inlining)优化,并确保函数满足“无副作用”这一前提。这并非 TypeScript 或 ESLint 的职责,而是由底层编译/构建工具链(如 TypeScript 编译器 tsc 配合目标 JS 引擎、或更底层的 webpack + Terser、Vite 构建时的压缩器、甚至 g++/clang 对 C++ 的处理)在生成最终代码时完成。下面分场景说明关键配置点:
✅ 明确适用范围:哪些工具真正支持“自动行内展开”
-
TypeScript 本身不执行函数内联:
tsc是类型检查+转译器,它只做语法降级(如async/await→Promise),不进行运行时优化,也不会把const add = (a, b) => a + b展开成a + b。 -
真正做内联的是 JS 压缩器(如 Terser)或 AOT 编译器(如 GraalVM、Rust 的
rustc -C opt-level=3)
所以你需要在打包/生产构建阶段启用对应优化。
✅ 关键配置项(按主流前端构建工具)
Webpack + TerserPlugin(默认已启用内联)
Webpack 5+ 内置 TerserPlugin,只要开启 mode: 'production',就会自动:
- 识别
const fn = (x) => x * 2这类纯函数(无闭包捕获、无副作用、非递归、体积小) - 在压缩时将其调用处直接替换为表达式(即“行内展开”)
可显式强化配置(.webpack.config.js):
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
inline: 3, // 启用深度内联(0=禁用,1=简单,3=激进)
passes: 2, // 多轮压缩提升内联机会
},
mangle: true,
}
})
]
}⚠️ 注意:
inline: 3要求函数体足够简单(通常 ≤ 5 行、无try/catch、无this、无arguments)。复杂函数不会被内联,避免破坏语义。
Vite(基于 esbuild + terser)
Vite 生产构建默认启用 build.minify: 'esbuild'(速度快但内联保守)或 'terser'(更激进)。推荐显式指定:
// vite.config.ts
export default defineConfig({
build: {
minify: 'terser',
terserOptions: {
compress: {
inline: 3,
drop_console: true,
}
}
}
})TypeScript + tsc(仅转译,不内联 → 需配合后续步骤)
tsc 本身不做内联,但你可以通过 --noEmit 禁用输出,改由 esbuild 或 swc 接管:
// tsconfig.json
{
"compilerOptions": {
"noEmit": true,
"isolatedModules": true
}
}然后用 esbuild --minify --tree-shaking --pure:fnName(--pure 标记无副作用函数名,强制内联)。
✅ 如何标记“无副作用”以便工具识别
某些工具(如 Terser、esbuild)支持通过注释或配置显式声明:
-
JSDoc 注释(推荐,跨工具兼容):
/** * @__PURE__ */ export const clamp = (n: number, min: number, max: number) => n < min ? min : n > max ? max : n;
@__PURE__提示压缩器:此函数调用可安全移除或内联,即使未被直接引用。 -
esbuild 的
--pure参数:esbuild src/index.ts --minify --pure:clamp --pure:identity
✅ 验证是否生效
编译后查看生成的 .js 文件(非 sourcemap):
- 源码中
clamp(x, 0, 100)应变成x<0?0:x>100?100:x - 若仍保留函数调用,则检查:函数是否含
console.log、Date.now()、Math.random()等副作用;是否被动态调用(如fnMap[key]());是否跨模块未export清晰。
不复杂但容易忽略


















