如何导出包作用域JSDoc类型?解决tsc类型作用域两难问题
TypeScript 类型作用域两难问题解决方案探索
我正在排查TypeScript的#50436号bug,遇到以下核心问题:
问题1:包内全局类型无法被外部访问
- 在
./types.js中声明类型(已加入jsconfig.json的include数组)时,类型属于包作用域,包内所有文件可直接使用:
// types.js /** * @typedef {Object} Foo * @prop {String} name */ // index.js /** @type {Foo} */ let foo;
- 但外部包无法通过导入获取该类型:
// bar.js /** @type {import('foo/types.js').Foo} */ let foo; // ❌ [tsserver] Cannot find name 'Foo'.
问题2:导出的类型无法在包内全局使用
- 给
./types.js添加导出语句后,外部包可以正常导入类型,但包内无法再直接引用:
// types.js module.exports = {}; // 触发类型导出 /** * @typedef {Object} Foo * @prop {String} name */ // index.js /** @type {Foo} */ let foo; // ❌ [tsserver] Cannot find name 'Foo'.
- 外部包导入正常:
// bar.js /** @type {import('foo/types.js').Foo} */ let foo;
测试用例结构
测试用例包含完整配置文件,目录结构如下:
. ├── README.md ├── main.js ├── tsconfig.json └── node_modules ├── bar │ ├── bar.js │ ├── package.json │ ├── tsconfig.json │ └── types.js └── foo ├── foo.js ├── package.json ├── tsconfig.json └── types.js
核心需求
如何同时实现:
- 包内文件无需导入,可直接使用类型
- 外部包可通过导入访问该类型
可行解决方案
方案1:全局声明+模块导出结合
在类型文件中,通过declare global将类型挂载到包内全局作用域,同时保留导出语句供外部使用:
// types.js module.exports = {}; /** * @typedef {Object} Foo * @prop {String} name */ // 将类型注入包内全局作用域 declare global { type Foo = import('./types.js').Foo; }
注意:需确保tsconfig.json的include包含该文件,或在compilerOptions.types中指定。
方案2:拆分类型文件
创建两个类型文件,分离包内全局使用和外部导出的逻辑,避免重复代码:
./internal-types.js(包内全局用,无导出):
/** * @typedef {Object} Foo * @prop {String} name */
./types.js(导出类型供外部用):
/** * @typedef {import('./internal-types.js').Foo} Foo */ module.exports = {};
在jsconfig.json的include中加入internal-types.js,包内直接用Foo,外部通过import('foo/types.js').Foo访问。
方案3:JSDoc @global标签
在类型声明上添加@global标签,让类型同时属于全局作用域和模块导出:
// types.js module.exports = {}; /** * @typedef {Object} Foo * @global * @prop {String} name */
包内文件可直接引用Foo,外部包通过导入正常访问。
内容的提问来源于stack exchange,提问作者root-aj
相关产品推荐
相关产品推荐

