关键在于以模块边界为契约防线,仅暴露稳定接口、私有化内部状态、用命名导出+TS约束封堵猜测式引用、工具链静态分析拦截越界引用,并通过api.ts统一门禁式导出实现升级无感。

要利用 exports 的强制封装特性规避“错误引用内部 API 导致的变量升级隐患”,关键在于把模块边界当作契约防线,而非语法开关。隐患本质不是导出写错了,而是外部模块绕过约定、直接读取未声明为公开的变量(比如 __cache、_config、internalUtils),后续你升级内部实现时一改就崩。
只暴露稳定接口,不暴露任何实现路径
变量升级隐患常始于外部代码偷偷依赖了临时状态或调试字段。例如:
- ❌ 错误示范:在
payment.ts中写export const _retryCount = 3;,其他模块直接修改它——下次你把它改成配置项或移入 class 实例,调用方立刻报错 - ✅ 正确做法:所有内部状态必须私有化,仅通过函数接口控制访问。如提供
export function setMaxRetries(n: number)和export function getMaxRetries(): number,外部无法直读/直写 - 对配置类变量,统一走初始化函数,不导出原始对象:
export const initPayment = (options: PaymentOptions) => { ... },而不是export const defaultConfig = {...}
用命名导出 + 类型约束封死“猜测式引用”
开发者常靠名字猜功能(比如看到 formatDateHelper 就 import 使用),结果你某天重命名或删掉它,项目就挂。命名导出配合 TypeScript 可提前拦截这类误用:
- 导出项全部用语义明确的驼峰名,如
export const validateCardNumber,禁用export const helper1或export const utils这类模糊命名 - 在
.d.ts或 JSDoc 中标注@internal的辅助函数,TS 编译器会拒绝导入;也可用declare module "xxx" { export const __private: never; }主动声明不可访问项 - 启用 ESLint 规则
import/no-restricted-paths,禁止跨层引用底层目录(如禁止reporting模块直接 importpayment/core/encrypt.ts)
构建时静态分析兜底,让“越界引用”无处藏身
光靠人写得规范不够,要用工具链自动识别并阻断非法引用:
- 开启 TypeScript 的
noUnusedExports,自动剔除未被顶层api.ts转发的导出项,避免“写出来但没人该用”的幽灵变量残留 - 用
depcheck或自定义 AST 扫描,在 CI 阶段检查是否出现import * as internal from './utils/internal'这类违规路径 - 对高危模块(如支付、权限)加构建守卫:若检测到非白名单模块引用其私有路径,直接中断构建并提示“请通过 paymentService.submit() 调用”
用 re-export 构建逻辑门禁,让顶层成为唯一入口
真正的封装不是“我不导出”,而是“你只能从我指定的门进来”。所有业务模块应设一个 api.ts 作为出口闸机:
- 底层文件(
./adapters/、./core/)一律不对外暴露,连测试都只能通过api.ts访问 -
api.ts不做简单转发,而是做语义包装:export const submitOrder = wrapWithTimeout(orderCore.submit),把内部函数名、参数结构完全隔离 - 若某变量未来需升级(如从 number 改为 object),只需在
api.ts层做兼容转换,外部调用完全无感
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










