首页 / TypeScript 入门教程 / 声明文件(.d.ts)入门

TypeScript 入门教程

声明文件(.d.ts)入门

本教程共 80 篇 · 第 61 篇 · 更新于 2026-08-10 · 约 12 分钟阅读

TypeScriptTypeScript 入门教程声明文件.d.tsdeclare类型定义

本节目标:搞清楚 .d.ts 文件是什么、为什么需要它、declare 关键字的用法,以及声明文件和普通 .ts 文件的区别。学完你就能看懂一个第三方声明文件在说什么。

一个没有类型的 JS 库

假设你从 npm 装了一个叫 math-utils 的包,它的 index.js 长这样:

// math-utils/index.js
function add(a, b) {
  return a + b;
}

function multiply(a, b) {
  return a * b;
}

module.exports = { add, multiply };

你在 TypeScript 项目里这样用:

import { add } from "math-utils";

add(1, 2); // ✅ 运行时没问题
add("hello", "world"); // ✅ 运行时也能跑,但结果不是你要的

问题来了——TypeScript 编译器不认识 math-utils。它不知道这个模块导出什么、add 的参数是什么类型、返回值是什么类型。编辑器不会给你补全,也不会报类型错误。

声明文件就是来解决这个问题的。它给 JavaScript 代码”贴上类型标签”,让 TypeScript 编译器知道一个 JS 变量、函数、类、模块的类型信息。

.d.ts 是什么

.d.ts 是 TypeScript 的类型声明文件(Declaration File)。它和 .ts 文件的核心区别:

.ts.d.ts
内容类型 + 运行时逻辑仅类型声明,不含实现
编译产出被编译为 .js不产生 .js 文件
运行时代码完全没有
用途写业务逻辑描述已有 JS 代码的类型

一句话总结:.d.ts 文件只对编译器说话,不对运行时说话。你可以把它理解为”类型世界的说明书”。

declare 关键字

declare 是声明文件的基石。它的作用是告诉 TypeScript:“这个变量/函数/类已经存在于运行时,我只是在描述它的类型,不用检查它的实现。”

以下面的代码为例——在 .d.ts 文件里,所有声明都必须加 declare(或 export):

declare var / let / const

// 声明一个全局变量
declare var globalCounter: number;

// 只读的全局常量
declare const MAX_SIZE: number;

// 块级作用域的变量
declare let currentUser: string;

constlet 的区别跟 TypeScript 普通代码一样:const 声明的变量不能被重新赋值。

Tip

如果一个变量确实不会被重新赋值,优先用 declare const。这给下游代码提供了更强的类型安全——谁尝试修改它,编译器就报错。

declare function

// 声明一个全局函数
declare function greet(name: string): string;

// 支持重载——多个 declare function 声明同一个名字
declare function getValue(key: string): string;
declare function getValue(key: number): number;

注意,declare function 只写函数签名,不写函数体。函数体在你的 JS 代码里,声明文件不需要知道它怎么实现的。

declare class

declare class Greeter {
  constructor(message: string);

  greeting: string;
  showGreeting(): void;
}

这里声明了一个类,它有一个构造器、一个属性和一个方法。编译器会根据这个声明来检查你创建 Greeter 实例的代码是否正确——但 .d.ts 不会把 class 编译成任何 JS 代码。

declare enum

declare enum Direction {
  Up,
  Down,
  Left,
  Right
}

枚举有点特殊:它既是类型也是值。declare enum 告诉 TS 这个枚举的运行时值已经存在于 JS 侧。

declare namespace

declare namespace MyLib {
  function makeGreeting(s: string): string;
  let numberOfGreetings: number;
}

namespace 用来组织一组相关的声明。上面的代码等价于告诉 TS:“有一个叫 MyLib 的全局对象,它上面有 makeGreeting 方法和 numberOfGreetings 属性。”

你可以通过点号访问:

MyLib.makeGreeting("hello");
console.log(MyLib.numberOfGreetings);
Note

TS 7.0 仍然支持 namespace(内部模块)语法,但它主要用于声明文件中组织类型。在业务代码里,推荐用 ES 模块(import/export)代替 namespace

全局声明 vs 模块声明

声明文件分两种身份:

全局声明文件(脚本模式):文件顶层没有 import/export,所有 declare 的内容直接进入全局作用域。任何 .ts 文件都能直接用这些名字,不需要显式导入。

// global.d.ts —— 无 import/export,声明进入全局
declare function alert(message: string): void;
declare const VERSION: string;

模块声明文件(模块模式):文件顶层有 importexport,声明只在模块内可见。外部要通过 import 使用。

// math-utils.d.ts —— 有 export,声明是模块内的
export function add(a: number, b: number): number;
export function multiply(a: number, b: number): number;

这块在后续章节会详细展开,现在只需要知道有这个区别。

编译器如何发现声明文件

你可能会想:我写了个 .d.ts 放项目里,TypeScript 怎么知道要读它?

TS 编译器按以下优先级查找声明文件:

  1. tsconfig.jsoninclude / files 配置:如果你的声明文件在这些字段指定的路径下,TS 会直接加载。
  2. types 字段:在 TS 7.0 中,types: [] 是默认值(6.x 及以前默认加载所有可见的 @types/*)。你需要显式指定需要的类型包。
  3. typeRoots 字段:自定义声明文件的查找目录,默认为 node_modules/@types
  4. 三斜线指令/// <reference types="..." />/// <reference path="..." />(旧写法,不推荐在新项目中使用)。

举个例子——如果你在项目根目录放了一个 custom.d.ts

{
  "compilerOptions": {
    "strict": true,
    "types": []
  },
  "include": ["src/**/*", "custom.d.ts"]
}

这样 custom.d.ts 里的全局声明就能在 src/ 下的所有文件中直接使用。

声明文件不包含什么

理解 “.d.ts 里不能写什么” 跟会写同等重要:

  • 不能写函数体:声明文件只描述签名,不提供实现。declare function fn(x: number): number { return x; } 是错误的。
  • 不能写可执行代码console.log、变量赋值、条件语句等都禁止。
  • 不能直接引入 .js 运行时代码:声明文件是纯类型层面的事。

如果你在 .d.ts 里写了运行时逻辑,编译器直接报错。

一个完整的简单示例

假设有一个 math-utils 库,提供了以下全局 API:

// math-utils.js(运行时代码)
var MathUtils = {
  version: "1.0.0",
  add: function(a, b) { return a + b; },
  multiply: function(a, b) { return a * b; },
  Calculator: function(initial) {
    this.value = initial || 0;
  }
};

MathUtils.Calculator.prototype.add = function(n) {
  this.value += n;
  return this;
};

对应的声明文件:

// math-utils.d.ts(类型描述)
declare namespace MathUtils {
  const version: string;

  function add(a: number, b: number): number;
  function multiply(a: number, b: number): number;

  class Calculator {
    constructor(initial?: number);
    value: number;
    add(n: number): this;
  }
}

现在任何 TypeScript 文件里使用 MathUtils 都能获得完整的智能提示和类型检查。

接下来

入门内容就到这。你已经知道了 .d.ts 文件的基本语法:declare var/function/class/enum/namespace 和它们的用途。下一章我们开始写真正的全局库声明文件,从命名空间的结构到完整模板——把今天学的基本招式组装成一套完整的声明方案。