在使用 TypeScript 开发大型应用或库时,.d.ts
文件(即声明文件)扮演着至关重要的角色。它们用于提供类型信息、提升代码的可读性和可维护性,特别是在与 JavaScript 模块、第三方库或非 TypeScript 代码交互时。本文将总结 .d.ts
文件的常见放置位置、src/types
与 src/@types
的区别、系统预置的 lib.d.ts
文件,以及推荐的最佳实践。
一、.d.ts
文件的主要用途
- 为 第三方 JavaScript 库 提供类型声明(如没有官方或 DefinitelyTyped 的类型)。
- 为 现有 JavaScript 项目 增加类型支持。
- 封装模块导出类型,例如发布 npm 包时提供的类型信息。
- 定义 全局类型、模块、命名空间、扩展原生对象等。
二、.d.ts
文件的放置位置与组织策略
1. 放在 src/types
目录中
这是最常见的做法,适合用于存放项目内自定义的公共类型、接口、工具类型等。例如:
my-project/
├── src/
│ ├── index.ts
│ └── types/
│ ├── global.d.ts
│ └── user.d.ts
推荐的 tsconfig.json
配置:
{
"compilerOptions": {
"typeRoots": ["./node_modules/@types", "./src/types"]
},
"include": ["src"]
}
如果不配置 typeRoots
,只要 .d.ts
文件位于 include
范围中,TypeScript 都能正确识别。
2. 放在 src/@types
目录中
该目录名称借鉴了社区对 @types
的命名约定,更适合用于补充或重写第三方库的类型。例如:
my-project/
├── src/
│ └── @types/
│ └── axios/
│ └── index.d.ts
这在结合 typeRoots
使用时特别有用:
{
"compilerOptions": {
"typeRoots": ["./src/@types"]
}
}
注意:
typeRoots
中的目录会被 TypeScript 当作“类型包”,要求子目录是包名,并包含index.d.ts
,否则不会生效。
✅ 对比总结
用途 | 推荐目录 | 理由 |
---|---|---|
定义全局类型、接口、工具类型 | src/types | 更语义化、简洁明了 |
扩展第三方库(module augmentation) | src/@types/<包名>/index.d.ts | 模仿 @types 结构,更规范 |
无 typeRoots 配置时 | 任意包含在 include 内的目录 | TypeScript 会自动识别 |
使用了 typeRoots | 需要是包结构(子目录名即包名) | 否则不会识别非包目录下的 .d.ts 文件 |
三、模块声明与增强(module augmentation)
当你需要为第三方库扩展类型时,可以通过模块增强进行定义:
// src/@types/axios/index.d.ts
import 'axios'
declare module 'axios' {
interface AxiosInstance {
customMethod(): void;
}
}
确保文件命名与包名一致,并使用 declare module '模块名'
,否则不会生效。
四、npm 包中的类型声明
如果你在开发 npm 库并希望提供类型信息,有两种主流方式:
1. 自动生成声明文件
// tsconfig.json
{
"compilerOptions": {
"declaration": true,
"outDir": "dist"
}
}
确保 package.json
中设置:
{
"types": "dist/index.d.ts"
}
2. 手动维护类型文件
对于结构复杂或混合 JS 的项目,可使用手写的 .d.ts
文件作为 types
指向目标。
五、最佳实践总结
目标 | 推荐做法 |
---|---|
项目自定义类型 | 使用 src/types/ ,例如 user.d.ts , api.d.ts |
扩展第三方库 | 使用 src/@types/<库名>/index.d.ts |
避免全局污染 | 不要随意使用全局声明,优先用模块导入导出 |
使用 typeRoots | 子目录需为包名结构,含 index.d.ts |
发布库时 | 配置 declaration: true 并指定 types 字段 |
团队项目 | 统一约定类型目录结构并文档化,避免混乱 |
六、常见陷阱与注意事项
.d.ts
中没有export
或import
会被视为全局类型。typeRoots
会覆盖默认值(如node_modules/@types
),需谨慎配置。- 模块增强需配合原始模块名使用
declare module
。 - 使用路径映射(paths)时,需确保类型声明路径同步。
七、系统预设的 lib.d.ts
文件简介
除了项目中的自定义声明文件,TypeScript 还自带了一套标准库类型声明文件,称为 “标准库类型定义(lib.d.ts)”。这些文件包含了浏览器、ES 标准、DOM、WebWorker、ESNext 等环境中内置对象和 API 的类型信息。
📁 文件位置
这些声明文件随着 TypeScript 安装自动提供,通常位于:
node_modules/typescript/lib/lib.*.d.ts
例如:
lib.es5.d.ts
:包含Array
,Promise
,Function
等基础类型。lib.dom.d.ts
:包含Document
,HTMLElement
,Event
,Window
等浏览器 DOM 类型。lib.webworker.d.ts
:包含WorkerGlobalScope
,postMessage
,onmessage
等。lib.esnext.array.d.ts
:如Array.prototype.flatMap
,Object.fromEntries
等新提案类型。
⚙️ 配置方式(tsconfig.json
中的 lib
字段)
默认情况下,TypeScript 会根据 target
自动推导需要加载的标准库类型文件。但你也可以手动指定:
{
"compilerOptions": {
"target": "es5",
"lib": ["es5", "dom"]
}
}
常见选项有:
"es5"
/"es6"
/"es2017"
/"esnext"
等:控制 ECMAScript 语言级别。"dom"
:提供浏览器 API 支持(如document
,window
等)。"webworker"
:支持 Worker 相关 API。"scripthost"
:支持WScript
、ActiveXObject
等宿主环境。"dom.iterable"
:为 DOM 集合提供Symbol.iterator
支持。
⚠️ 如果你显式设置了
lib
,TypeScript 不会自动添加默认值,需列出全部所需库。
✅ 建议配置
项目环境 | 建议配置 |
---|---|
前端浏览器项目 | "lib": ["dom", "esnext"] |
仅 Node.js 后端 | "lib": ["esnext"] |
Web Worker 脚本 | "lib": ["webworker", "esnext"] |
精简运行环境 | 明确列出所需 lib ,避免多余类型干扰 |
八、结语
良好的类型声明组织不仅包括 .d.ts
文件的结构和分布,也包括对系统预设类型的理解和管理。TypeScript 提供了灵活的 typeRoots
和 lib
配置机制,结合良好的团队规范,可以显著提高项目的可维护性和开发效率。