TypeScript 模块声明怎么写
很多刚接触TypeScript的开发者都有过这样的经历:项目里引入了第三方的JS工具库,运行时明明能正常用,编辑器却飘出一堆“找不到模块”“类型声明文件缺失”的红色波浪线——这就是模块声明没写对惹的祸。简单说,模块声明就是给JS库“补”上TypeScript能看懂的“说明书”,让TS知道这个模块有啥功能、该怎么用,是TS项目里衔接JS生态和类型系统的关键小工具。
最基础的情况:直接声明陌生模块
要是你用的是没自带类型定义的第三方JS库,比如一个处理日期格式化的小工具date-utils,最省事的就是用declare module直接声明模块。举个生活化的例子:就像你收到一个没说明书的新耳机,你只知道它能插手机听歌、能调音量,不用写复杂描述,只说清楚基本功能就行。

具体写法是:新建一个xxx.d.ts文件(文件名后缀必须是.d.ts,这是TS识别声明文件的标志),里面写declare module "date-utils",大括号里描述模块的导出内容。比如这个工具导出了两个函数,一个格式化日期、一个计算天数差,就这么写:
`typescript
declare module "date-utils" {
export function format(date: Date, pattern: string): string;
export function diffDays(date1: Date, date2: Date): number;
}
`
写完之后,编辑器的红色波浪线基本就消了,以后你用import { format } from "date-utils"时,TS还会自动检查你传的参数对不对——比如你给format传了数字类型的第一个参数,TS会立刻报错,提醒你应该传Date类型,这就是类型声明的作用。
进阶场景:声明带子路径的模块
有些工具库会做模块化拆分,比如一个UI库awesome-ui,既可以引入整体import * as AwesomeUI from "awesome-ui",也可以单独引入按钮组件import Button from "awesome-ui/button"。这时候基础的模块声明就不够用了,得加个通配符*来匹配子路径,就像你给快递员写地址,主地址是“小区”,子地址是“3号楼2单元”,通配符就是让快递员不管送哪栋都能找到。
写法是在声明里用"awesome-ui/*"匹配所有子路径:
`typescript
declare module "awesome-ui/*" {
const Component: React.ComponentType<any>;
export default Component;
}
`
这样一来,你引入awesome-ui/button、awesome-ui/input这类子路径时,TS也能正常识别类型,不会再飘红。
特殊情况:声明全局模块或UMD库
还有两种常见场景需要特殊处理:一种是纯全局挂载的库,比如jQuery,直接用$挂载到window上;另一种是UMD格式的库,兼容浏览器和Node环境。
比如jQuery,你可以在声明里直接写全局变量:
`typescript
declare const $: (selector: string) => any;
`
这样在页面里写$(".btn")时,TS就不会报错。要是你用的是UMD库,需要考虑它在不同环境的导出,这时候可以用declare module "lib-name" { ... }里的export =语法,让TS兼容CommonJS和ES模块的导入方式,就像给跨平台的工具加了适配层。
最后要注意,模块声明文件必须放在TS能识别的地方,一般项目里建个@types文件夹专门放自定义声明,或者在tsconfig.json里配置typeRoots指定声明文件的位置,不然TS找不到就白写了。