发布到npm的TypeScript项目中,.d.ts文件是否必须使用export语句?
问题描述
我有一个应用,希望将其中部分代码复用至另一个项目。该应用的.d.ts文件中定义了许多全局可见的接口,例如typings.d.ts文件中定义了如下接口:
interface Something { thing: string }
随后我编写了一个使用该接口的组件:
export function setSomething(input: Something){ input.thing = 'thing'; }
这段代码在原应用中运行正常,但发布到npm后,当其他项目通过如下方式导入组件时:
import {setSomething} from 'ts-test/component'; setSomething({thing: 'nothing'});
TypeScript类型检查无法找到该接口并报错。将所有接口从.d.ts文件导出并在.ts文件中导入可解决问题,但需要修改大量文件,请问能否避免?为何父项目不会自动加载依赖中自动加载的类型声明?
我曾尝试在package.json中添加"types": "./types.d.ts",但类型检查报错提示types.d.ts不是模块;也尝试过配置tsconfig的"include": ["*.ts", "typings.d.ts"],但该配置对安装此包的项目无效。
解决方案与原因解析
一、为什么父项目不会自动加载依赖的全局类型?
- TypeScript依赖类型加载规则:TypeScript只会自动加载满足以下条件的依赖包类型:
- 依赖包的
package.json中指定了types/typings字段,且指向的文件是合法的类型声明文件; - 依赖包根目录存在
@types目录(通常用于第三方类型定义)。
它不会主动扫描依赖包内的所有.d.ts文件,所以你原项目里的局部全局类型文件,若未正确配置,父项目无法感知。
- 依赖包的
- tsconfig的
include仅作用于本地项目:你在自己项目tsconfig中配置的include只会影响当前项目的编译范围,依赖项目的TypeScript不会读取你的包的tsconfig配置,因此该设置对父项目无效。
二、无需修改大量文件的解决方案
方案1:将全局类型文件转换为"模块式全局声明"(推荐)
你不需要修改原有组件代码,只需调整typings.d.ts的结构,让它既能作为模块被TypeScript识别,又能保留全局接口的可见性:
// typings.d.ts export {}; // 标记为模块,解决"不是模块"的报错 declare global { interface Something { thing: string; } // 其他全局接口也放在这里 }
然后在你的包的package.json中配置:
{ "types": "./typings.d.ts", // 确保发布时包含该文件,可通过files字段指定 "files": ["component.js", "component.d.ts", "typings.d.ts"] }
发布后,父项目安装你的包时,TypeScript会自动加载typings.d.ts,其中的全局接口会被注入到父项目的全局作用域,组件代码无需任何修改即可正常使用类型。
方案2:让父项目配置typeRoots(不推荐,需依赖方配合)
如果不想修改自己的包,可以让父项目在tsconfig.json中把你的包目录加入typeRoots,强制TypeScript扫描其中的类型文件:
{ "compilerOptions": { "typeRoots": ["./node_modules/@types", "./node_modules/ts-test"] } }
但该方案需要每个使用你的包的项目都手动配置,通用性较差。
三、之前尝试的问题解析
types字段报错"不是模块":因为你原来的typings.d.ts是纯全局类型文件(无任何import/export),TypeScript要求types字段指向的文件要么是模块,要么是包含全局声明的合法文件。添加export {}将其标记为模块,再用declare global包裹接口,即可解决该报错。include配置无效:如前所述,include是项目本地编译配置,仅对当前项目生效,无法影响依赖包的类型加载逻辑。
内容的提问来源于stack exchange,提问作者Dmay
相关产品推荐
相关产品推荐

