模块 .d.ts

JavaScript 与示例 DTS 的对比

常见的 CommonJS 模式

使用 CommonJS 模式的模块会使用 module.exports 来描述导出的值。例如,下面是一个导出一个函数和一个数值常量的模块:

js
const maxInterval = 12;
function getArrayLength(arr) {
return arr.length;
}
module.exports = {
getArrayLength,
maxInterval,
};

这可以用下面的 .d.ts 来描述:

ts
export function getArrayLength(arr: any[]): number;
export const maxInterval: 12;

TypeScript Playground 可以向你展示 JavaScript 代码对应的 .d.ts。你可以在这里亲自试一试。

.d.ts 语法有意设计成与 ES 模块语法相似。ES 模块于 2015 年作为 ES2015(ES6)的一部分由 TC39 批准,尽管它早已通过转译器可用;不过,如果你的 JavaScript 代码库使用的是 ES 模块:

js
export function getArrayLength(arr) {
return arr.length;
}

它对应的 .d.ts 如下:

ts
export function getArrayLength(arr: any[]): number;

默认导出

在 CommonJS 中,你可以将任何值作为默认导出来导出,例如下面是一个正则表达式模块:

js
module.exports = /hello( world)?/;

它可以用下面的 .d.ts 来描述:

ts
declare const helloWorld: RegExp;
export = helloWorld;

或者一个数字:

js
module.exports = 3.142;
ts
declare const pi: number;
export = pi;

CommonJS 中的一种导出方式是导出函数。由于函数也是对象,因此可以为其添加额外字段,这些字段也会包含在导出中。

js
function getArrayLength(arr) {
return arr.length;
}
getArrayLength.maxInterval = 12;
module.exports = getArrayLength;

它可以用以下内容来描述:

ts
declare function getArrayLength(arr: any[]): number;
declare namespace getArrayLength {
declare const maxInterval: 12;
}
export = getArrayLength;

有关其工作原理的详细信息,请参阅模块:函数以及模块参考页面。

处理多种消费方导入

现代消费方代码中有多种导入模块的方式:

ts
const fastify = require("fastify");
const { fastify } = require("fastify");
import fastify = require("fastify");
import * as Fastify from "fastify";
import { fastify, FastifyInstance } from "fastify";
import fastify from "fastify";
import fastify, { FastifyInstance } from "fastify";

要覆盖所有情况,需要 JavaScript 代码真正支持所有这些模式。为支持其中多种模式,CommonJS 模块需要大致写成这样:

js
class FastifyInstance {}
function fastify() {
return new FastifyInstance();
}
fastify.FastifyInstance = FastifyInstance;
// Allows for { fastify }
fastify.fastify = fastify;
// Allows for strict ES Module support
fastify.default = fastify;
// Sets the default export
module.exports = fastify;

模块中的类型

你可能想为尚不存在的 JavaScript 代码提供类型

js
function getArrayMetadata(arr) {
return {
length: getArrayLength(arr),
firstObject: arr[0],
};
}
module.exports = {
getArrayMetadata,
};

它可以用以下内容来描述:

ts
export type ArrayMetadata = {
length: number;
firstObject: any | undefined;
};
export function getArrayMetadata(arr: any[]): ArrayMetadata;

这个示例很适合使用泛型来提供更丰富的类型信息:

ts
export type ArrayMetadata<ArrType> = {
length: number;
firstObject: ArrType | undefined;
};
export function getArrayMetadata<ArrType>(
arr: ArrType[]
): ArrayMetadata<ArrType>;

现在数组的类型会传递到 ArrayMetadata 类型中。

然后,模块的使用者可以在 TypeScript 代码中使用 import 或 import type,或者通过 JSDoc 导入来复用所导出的类型。

模块代码中的命名空间

描述 JavaScript 代码的运行时关系可能很棘手。当类 ES 模块语法无法提供足够的工具来描述导出时,你可以使用 namespaces。

例如,你可能有一些类型足够复杂,以至于你选择在 .d.ts 中把它们放入命名空间:

ts
// This represents the JavaScript class which would be available at runtime
export class API {
constructor(baseURL: string);
getInfo(opts: API.InfoRequest): API.InfoResponse;
}
// This namespace is merged with the API class and allows for consumers, and this file
// to have types which are nested away in their own sections.
declare namespace API {
export interface InfoRequest {
id: string;
}
export interface InfoResponse {
width: number;
height: number;
}
}

要了解命名空间在 .d.ts 文件中的工作方式,请阅读 .d.ts 深入探究。

可选的全局用法

你可以使用 export as namespace 声明你的模块在 UMD 上下文中将在全局作用域中可用:

ts
export as namespace moduleName;

参考示例

为了让你了解这些部分如何组合在一起,下面是一个在创建新模块时可供起步的参考 .d.ts

ts
// Type definitions for [~THE LIBRARY NAME~] [~OPTIONAL VERSION NUMBER~]
// Project: [~THE PROJECT NAME~]
// Definitions by: [~YOUR NAME~] <[~A URL FOR YOU~]>
/*~ This is the module template file. You should rename it to index.d.ts
*~ and place it in a folder with the same name as the module.
*~ For example, if you were writing a file for "super-greeter", this
*~ file should be 'super-greeter/index.d.ts'
*/
/*~ If this module is a UMD module that exposes a global variable 'myLib' when
*~ loaded outside a module loader environment, declare that global here.
*~ Otherwise, delete this declaration.
*/
export as namespace myLib;
/*~ If this module exports functions, declare them like so.
*/
export function myFunction(a: string): string;
export function myOtherFunction(a: number): number;
/*~ You can declare types that are available via importing the module */
export interface SomeType {
name: string;
length: number;
extras?: string[];
}
/*~ You can declare properties of the module using const, let, or var */
export const myField: number;

库的文件布局

你的声明文件的布局应当与库的布局保持一致。

一个库可以包含多个模块,例如

myLib
+---- index.js
+---- foo.js
+---- bar
+---- index.js
+---- baz.js

它们可以这样导入

js
var a = require("myLib");
var b = require("myLib/foo");
var c = require("myLib/bar");
var d = require("myLib/bar/baz");

因此,你的声明文件应当是这样

@types/myLib
+---- index.d.ts
+---- foo.d.ts
+---- bar
+---- index.d.ts
+---- baz.d.ts

测试你的类型

如果你打算把这些更改提交到 DefinitelyTyped 供大家使用,我们建议你:

  1. 在 node_modules/@types/[libname] 中创建新文件夹
  2. 在该文件夹中创建 index.d.ts,并把示例复制进去
  3. 查看你对模块的使用在何处出错,并开始补全 index.d.ts
  4. 当你满意后,克隆 DefinitelyTyped/DefinitelyTyped 并按照 README 中的说明操作。

否则

  1. 在源码树的根目录下创建新文件:[libname].d.ts
  2. 添加 declare module "[libname]" { }
  3. 把模板填入 declare module 的大括号内,然后查看你的用法在何处出错

TypeScript 文档是一个开源项目。欢迎提交 Pull Request,帮助我们改进这些页面 ❤

最后更新:2026 年 10 月 23 日