TypeScript 中 .d.ts 文件详解和最佳实践

2,580 阅读4分钟

在使用 TypeScript 开发大型应用或库时,.d.ts 文件(即声明文件)扮演着至关重要的角色。它们用于提供类型信息、提升代码的可读性和可维护性,特别是在与 JavaScript 模块、第三方库或非 TypeScript 代码交互时。本文将总结 .d.ts 文件的常见放置位置、src/typessrc/@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 中没有 exportimport 会被视为全局类型。
  • 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":支持 WScriptActiveXObject 等宿主环境。
  • "dom.iterable":为 DOM 集合提供 Symbol.iterator 支持。

⚠️ 如果你显式设置了 lib,TypeScript 不会自动添加默认值,需列出全部所需库。

✅ 建议配置

项目环境建议配置
前端浏览器项目"lib": ["dom", "esnext"]
仅 Node.js 后端"lib": ["esnext"]
Web Worker 脚本"lib": ["webworker", "esnext"]
精简运行环境明确列出所需 lib,避免多余类型干扰

八、结语

良好的类型声明组织不仅包括 .d.ts 文件的结构和分布,也包括对系统预设类型的理解和管理。TypeScript 提供了灵活的 typeRootslib 配置机制,结合良好的团队规范,可以显著提高项目的可维护性和开发效率。