核心方式是使用 declare module 创建模块声明,准确描述 js 包导出的类型结构,支持 default、命名、umd 等导出模式,并通过 tsconfig 配置使声明生效。

为没有类型定义的纯 JavaScript npm 包手动编写 TypeScript 类型补全,核心方式是使用 declare module 创建全局或路径映射的模块声明。这不是“给 JS 文件加类型”,而是告诉 TypeScript:“这个包导出什么,我来负责描述”。关键在于声明要准确、可维护,并能被正确识别。
基本语法:用 declare module 声明模块形状
在 .d.ts 文件(如 types/my-pkg.d.ts)中写:
// types/my-pkg.d.ts
declare module 'my-pkg' {
export const version: string;
export function doSomething(input: string): number;
export class Helper {
constructor(name: string);
run(): void;
}
}
这样在任意 .ts 文件中 import { doSomething } from 'my-pkg' 就能获得类型提示和检查。
处理常见导出模式:default、命名、混合、UMD
不同 JS 包导出方式不同,声明需匹配实际行为:
-
默认导出(CommonJS 或 ES Module):
若require('pkg')返回一个函数或对象,用export default:declare module 'pkg' { export default function foo(): void; } -
命名导出 + 默认导出共存:
常见于 Babel 转译后的包,需同时声明:export const named: string;<br>export default function main(): void;
-
UMD / 全局变量式(如通过 script 标签引入):
配合export as namespace支持全局访问:declare module 'pkg' { export as namespace Pkg; export function init(): void; }
之后可在非模块文件中直接用Pkg.init()。
让声明生效:三类加载方式与配置要点
TypeScript 需知道去哪里找你的 .d.ts 文件:
-
项目级声明(推荐):把
types/目录加入tsconfig.json的"typeRoots"或确保它在"include"中:"include": ["src/**/*", "types/**/*"] -
全局声明(不推荐用于包):放在项目根目录下
global.d.ts,但会污染全局命名空间,仅适合极简补全。 - 发布到 DefinitelyTyped(进阶):写好完整类型后,可向 DefinitelyTyped 提交 PR,供所有人复用。
实用技巧:快速反推类型 & 避免常见坑
不必凭空猜类型,可结合运行时观察:
- 在 Node.js 中
console.log(require('pkg'))查看真实结构; - 用 VS Code 打开包的
index.js,悬停查看 JSdoc 注释(很多库自带); - 避免写
any—— 即使暂时不确定,也先用unknown或最小接口(如{ data: string }); - 注意版本兼容性:在声明顶部加注释说明适配的包版本,例如
// @version 2.1.0。











