TypeScript如何实现模块差异化类型导出 区分内外访问权限
能否实现模块对内对外导出不同类型?
需求为模块导出可变对象时,内部访问权限比外部更宽松:模块内部可访问对象所有属性、可重新赋值导出对象,外部导入后仅允许访问指定公开属性,同时避免无意义的间接访问层和多余编译产物。
现有方案的问题
你尝试的几种方案都存在明显缺陷:
- 容器包装方案:需要额外维护
oContainer层,内部每次访问都要写oContainer.o,编译后会生成多余的getter/setter代码,无实际业务价值。 - 类型断言hack方案:类型收窄/放宽的效果仅在顶层作用域生效,函数内部访问私有属性依然会报类型错误,无法覆盖模块内所有访问场景。
- Proxy代理方案:运行时需要创建额外Proxy实例、手写全量捕获器,存在不必要的运行时开销,编译后会产生多余代码。
零额外开销的实现方式
完全不需要借助运行时包装,利用TypeScript的类型层面能力即可实现需求,编译后几乎不会产生多余代码:
// Module A // 内部使用的完整对象,所有模块内逻辑都操作该变量,拥有完整的读写权限 let internalO = { publiclyAvailable: 0, noTouchy: 0, }; // 对外导出的引用直接指向内部对象,通过类型断言仅暴露公开属性类型 // 这里的类型断言是纯类型层面操作,不会产生任何JS代码 export const o = internalO as { publiclyAvailable: number }; // 模块内访问不受任何类型限制,函数内部也可正常访问私有字段 const f = () => { console.log(internalO.noTouchy); // 正常访问,无类型报错 internalO.noTouchy = 2; // 正常修改私有字段 }; // 直接修改内部对象属性时,外部导入的o会同步拿到最新值,二者指向同一引用 const updateVal = () => { internalO.publiclyAvailable = 1; }; // 如果需要替换整个对象引用,仅需加一行极低成本的同步代码即可 const replaceO = (newObj: typeof internalO) => { internalO = newObj; Object.assign(o, newObj); };
// Module B import { o } from './A.js'; o.publiclyAvailable; // 正常访问 o.noTouchy; // 类型报错,符合预期
该方案的优势:
- 无多余的容器层、Proxy层,不存在额外的访问开销,
internalO.xxx编译后和直接访问o.xxx的输出完全一致 - 类型限制覆盖所有场景,不存在仅顶层作用域生效的问题
- 对外类型严格收窄,外部无法通过类型校验访问私有属性
- 除替换整个对象时的
Object.assign外,无任何多余编译产物。
内容的提问来源于stack exchange,提问作者Doofus
相关产品推荐
相关产品推荐

