讲解

.d.ts 文件只包含类型声明、不包含实现,是 TypeScript 世界的「类型说明书」。它的三大用途:一是给纯 JavaScript 库补类型(社区的 DefinitelyTyped 仓库为几万个 npm 包提供了 @types/xxx);二是描述全局变量和全局扩展(老脚本挂到 window 上的东西);三是库作者发布类型——tsc 的 declaration 选项会在构建时自动生成 .d.ts,使用者获得完整类型提示。

写声明文件的核心语法是 declare:declare function、declare const、declare class 表示「这个东西运行时存在,我只描述形状」。declare global { interface Window { ... } } 用于给全局对象打补丁(比如给 window 挂的统计脚本写类型)。模块形式的 .d.ts(文件里有 import/export)与普通模块规则一致;没有顶层导入导出的 .d.ts 是全局脚本声明,对整个项目生效——给 CSS、图片等静态资源写模块声明(declare module "*.css")就是全局声明的典型用法。

消费侧几乎零成本:npm install --save-dev @types/node 这类包安装后自动生效(types 字段指向 .d.ts);自己项目里的 .d.ts 只要被 include 覆盖就会被 tsc 读取。判断一个 npm 包是否自带类型:看 package.json 有没有 types/typings 字段,没有就去装对应的 @types 包。

示例

给一个「假设存在的 JS 工具」写声明,并在代码中使用(声明文件与使用文件一起编译):

// file: money.d.ts
export declare function formatCents(cents: number, currency?: string): string;
export declare function parseToCents(text: string): number;
// file: use-money.ts
import { formatCents, parseToCents } from "./money";

const cents: number = parseToCents("39.90");
const label: string = formatCents(cents, "CNY");
console.log(`${cents} 分 = ${label}`);

全局声明:描述运行时由别的脚本注入的全局变量,以及静态资源模块:

// file: globals.d.ts
declare const __APP_VERSION__: string;

declare module "*.txt" {
  const content: string;
  export default content;
}
// file: use-globals.ts
const version: string = __APP_VERSION__;
console.log(`当前版本:${version}`);

常见坑

  • 在 .d.ts 里写实现:声明文件不能有函数体、初始化值;写实现请回到 .ts。
  • 同名 .ts 与 .d.ts 并列:a.ts 和 a.d.ts 同时存在会让解析混乱,实现和声明二选一(库发布时 .d.ts 由 tsc 生成,不要手写同名文件)。
  • 全局声明污染:脚本式 .d.ts 对所有文件生效,名字撞车直接冲突——能写成模块就写成模块。
  • 装了 @types 还自己写:重复声明会合并或冲突报错,先 npm view @types/xxx 确认社区有没有现成的。

小结

.d.ts = 类型说明书:declare 描述形状、declare global 补全局、declare module 声明资源;@types/* 是社区类型仓库;库用 tsc --declaration 自动产出。下一章回到工程核心:tsconfig.json。