TypeScript 声明文件 .d.ts 有什么作用?
简化版
.d.ts 声明文件用来描述已有 JavaScript 代码或第三方库的类型形状,只提供类型信息,不提供运行时实现。它让 TypeScript 在不改 JS 源码的情况下获得类型检查和智能提示。
详细版
声明文件常见用途:
- 给纯 JavaScript 库补类型。
- 描述全局变量、全局函数。
- 声明 CSS Module、图片等非 TS 模块。
- 扩展已有模块类型。
示例:
declare module '*.module.css' {
const classes: Record<string, string>;
export default classes;
}
面试重点是:.d.ts 没有实现,只描述类型契约。
完整版教学
一、声明文件解决类型信息缺失问题
TypeScript 需要知道一个模块导出了什么、函数参数是什么、返回值是什么。
如果代码本身是 JavaScript,或者导入的是图片、CSS、全局 SDK,TS 默认可能不知道类型。
.d.ts 就是给这些运行时实体补一份类型说明书。
declare function track(event: string, payload?: object): void;
这行声明告诉 TS:运行时有一个 track 函数。
但它不会创建这个函数。
如果运行时没有真实 track,代码依然会报运行时错误。
二、declare 表示“外部已经存在”
declare 的意思是声明某个东西存在。
它只进入类型系统,不生成 JavaScript。
这很适合描述 CDN 注入的全局对象或宿主环境 API。
declare const APP_VERSION: string;
console.log(APP_VERSION);
流程如下:
构建或运行环境注入 APP_VERSION
-> .d.ts 告诉 TS 它是 string
-> 业务代码获得类型检查
如果只写声明,不做实际注入,浏览器里仍然会出现 ReferenceError。
三、声明模块能处理特殊导入
前端项目经常导入 CSS、图片、SVG。 这些文件不是 TypeScript 模块,但打包器能处理。 声明文件可以告诉 TS 这些导入的类型。
declare module '*.png' {
const src: string;
export default src;
}
declare module '*.module.css' {
const classes: Record<string, string>;
export default classes;
}
| 导入资源 | 常见类型 |
|---|---|
.png/.jpg | string URL |
.module.css | Record<string, string> |
.svg?component | 组件类型 |
.json | 由配置或推断决定 |
这类声明要和构建工具的真实行为一致。
四、第三方库类型从哪里来
很多库自带类型声明。
如果库没有自带声明,可以安装 @types/xxx。
再不行,项目里可以自己写局部声明。
npm install -D @types/lodash
TypeScript 会按配置和模块解析规则寻找声明文件。
常见位置包括包里的 types 字段、index.d.ts、node_modules/@types。
这也是为什么安装类型包后 IDE 立刻有提示。
五、模块扩展可以补充已有类型
声明文件还能做 module augmentation。 例如给第三方库的接口补字段,或者给 Express Request 扩展用户信息。 这在中后台和 Node 项目里很常见。
declare module 'some-lib' {
interface Options {
traceId?: string;
}
}
记忆钩子:
.d.ts是类型世界的合同,不是运行时代码的施工队。
扩展类型时要确保模块名写对。 否则声明不会合并到目标模块,可能变成一个新的无效声明。
六、写声明文件要避免 any 泛滥
有些同学为了让报错消失,会写 declare module 'xxx';。
这会把模块变成 any,短期安静,长期失去类型保护。
更好的做法是先声明常用 API,再逐步补全。
declare module 'legacy-lib' {
export function request(url: string): Promise<unknown>;
}
数字上看,先覆盖 3 个常用函数,比把整个库声明成 any 更有价值。 类型声明是边用边补的契约,不必一次完美,但不能完全放弃。
七、常见误区与追问
- 误区:
.d.ts会生成运行时代码。 声明文件只提供类型信息,不会生成实现。 - 误区:写了 declare 就能保证变量运行时存在。 declare 只是告诉 TS 外部存在,真实值仍要由环境提供。
- 误区:没有类型的库直接 declare module 就完事。 这样通常会退化成 any,应该尽量补出关键 API 类型。
- 追问:
@types包有什么作用? 它为没有内置类型的第三方库提供类型声明。 - 追问:CSS Module 为什么要声明? TS 不认识 CSS 文件导入,需要声明模块形状,打包器负责运行时处理。
- 追问:模块扩展有什么风险? 模块名写错或扩展位置不被 tsconfig 包含,声明就不会生效。
八、加强记忆
声明文件就记“只描述,不实现”。它描述全局变量、第三方库、特殊资源导入和模块扩展。回答时一定补运行时边界:.d.ts 让 TS 相信某东西存在,但真正存在要靠脚本、打包器或运行环境。