/blog/
/blog/one/
/blog/resume/
/blog/answer/
/blog/code/
/blog/know/
/blog/posts/1109. 航班预订统计/
/blog/posts/1143. 最长公共子序列/
/blog/posts/1221. 分割平衡字符串/
/blog/posts/1436. 旅行终点站/
/blog/posts/1480. 一维数组的动态和/
/blog/posts/1588. 所有奇数长度子数组的和/
/blog/posts/162. 寻找峰值/
/blog/posts/1646. 获取生成数组中的最大值/
/blog/posts/165. 比较版本号/
/blog/posts/166. 分数到小数/
/blog/posts/187. 重复的DNA序列/
/blog/posts/1894. 找到需要补充粉笔的学生编号/
/blog/posts/208. 实现 Trie (前缀树)/
/blog/posts/211. 添加与搜索单词 - 数据结构设计/
/blog/posts/212. 单词搜索 II/
/blog/posts/223. 矩形面积/
/blog/posts/229. 求众数 II/
/blog/posts/230. 二叉搜索树中第K小的元素/
/blog/posts/240. 搜索二维矩阵 II/
/blog/posts/260. 只出现一次的数字 III/
/blog/posts/273. 整数转换英文表示/
/blog/posts/282. 给表达式添加运算符/
/blog/posts/284. 顶端迭代器/
/blog/posts/292. Nim 游戏/
/blog/posts/299. 猜数字游戏/
/blog/posts/301. 删除无效的括号/
/blog/posts/326. 3的幂/
/blog/posts/335. 路径交叉/
/blog/posts/352. 将数据流变为多个不相交区间/
/blog/posts/36. 有效的数独/
/blog/posts/371. 两整数之和/
/blog/posts/38. 外观数列/
/blog/posts/407. 接雨水 II/
/blog/posts/405. 数字转换为十六进制数/
/blog/posts/430. 扁平化多级双向链表/
/blog/posts/437. 路径总和 III/
/blog/posts/447. 回旋镖的数量/
/blog/posts/453. 最小操作次数使数组元素相等/
/blog/posts/470. 用 Rand7() 实现 Rand10()/
/blog/posts/476. 数字的补数/
/blog/posts/502. IPO/
/blog/posts/517. 超级洗衣机/
/blog/posts/524. 通过删除字母匹配到字典里最长单词/
/blog/posts/528. 按权重随机选择/
/blog/posts/600. 不含连续1的非负整数/
/blog/posts/639. 解码方法 II/
/blog/posts/66. 加一/
/blog/posts/678. 有效的括号字符串/
/blog/posts/68. 文本左右对齐/
/blog/posts/704. 二分查找/
/blog/posts/725. 分隔链表/
/blog/posts/789. 逃脱阻碍者/
/blog/posts/787. K 站中转内最便宜的航班/
/blog/posts/869. 重新排序得到 2 的幂/
/blog/posts/881. 救生艇/
/blog/posts/797. 所有可能的路径/
/blog/posts/
/blog/posts/剑指 Offer 10- I. 斐波那契数列/
/blog/posts/剑指 Offer 22. 链表中倒数第k个节点/
/blog/posts/面试题 17.14. 最小K个数/
/blog/workshop/
/blog/answer/common/
/blog/answer/interview/
/blog/answer/invest/
/blog/answer/webRTC/01-前置知识/
/blog/answer/webRTC/02-RTCPeerConnection/
/blog/answer/webRTC/03-实践/
/blog/answer/webRTC/
/blog/code/lodash/01/
/blog/code/lodash/
/blog/code/nuxt/
/blog/code/npm/01-pify/
/blog/code/npm/02-downlaod/
/blog/code/npm/03-video.js/
/blog/code/npm/04-craco/
/blog/code/npm/05-axios/
/blog/code/npm/
/blog/code/opensource/01-第一期/
/blog/code/opensource/02-第二期/
/blog/code/opensource/04-第四期/
/blog/code/opensource/05-第五期/
/blog/code/opensource/07-第七期/
/blog/code/opensource/08-第八期/
/blog/code/opensource/09-第九期/
/blog/code/opensource/10-第十期/
/blog/code/opensource/11-第十一期 玩具vite/
/blog/code/opensource/12-第十二期 ni/
/blog/code/opensource/13-第十三期 open/
/blog/code/opensource/14-第十四期 promisify/
/blog/code/opensource/15-第十五期 element新增组件功能/
/blog/code/opensource/16-第十六期 一行代码统一规范 包管理器/
/blog/code/opensource/17-第十七期 js-cookie/
/blog/code/opensource/18-第十八期 delay/
/blog/code/opensource/21-第未知期/
/blog/code/opensource/
/blog/code/react/
/blog/code/vitepress/
/blog/know/back/
/blog/know/computer/
/blog/know/front/
/blog/know/javascript/
/blog/know/network/01-网路/
/blog/know/network/
/blog/know/tool/
/blog/know/typescript/
/blog/workshop/cli/01-项目创建/
/blog/workshop/cli/02-npm包发布/
/blog/workshop/cli/
/blog/workshop/windows11/
/blog/answer/common/business/vuepress搭建之旅/
/blog/answer/common/business/编写代码之前的思考/
/blog/answer/common/offer/03. 数组中重复的数字/
/blog/answer/common/interview/
/blog/answer/common/offer/04. 二维数组中的查找/
/blog/answer/common/offer/05. 替换空格/
/blog/answer/common/offer/07. 重建二叉树/
/blog/answer/common/offer/06. 从尾到头打印链表/
/blog/answer/common/offer/09. 用两个栈实现队列/
/blog/answer/common/offer/10- I. 斐波那契数列/
/blog/answer/common/offer/10- II. 青蛙跳台阶问题/
/blog/answer/common/offer/11. 旋转数组的最小数字/
/blog/answer/common/offer/
/blog/answer/common/offer/剑指 Offer 10- I. 斐波那契数列/
/blog/answer/common/offer/剑指 Offer II 069. 山峰数组的顶部/
/blog/answer/common/web/
/blog/answer/common/offer/剑指 Offer 22. 链表中倒数第k个节点/
/blog/answer/common/web/拖拽/
/blog/answer/common/web/类型判断/
/blog/answer/invest/book/
/blog/answer/invest/book/不可不知的经济真相/
/blog/answer/invest/book/投资第一课/
/blog/answer/invest/book/纳瓦尔宝典/
/blog/answer/interview/basic/01-html/
/blog/answer/interview/basic/02-css/
/blog/answer/interview/basic/03-javascript/
/blog/answer/interview/basic/
/blog/answer/interview/basic/vue/
/blog/code/react/react/
/blog/answer/webRTC/janus/01-init/
/blog/answer/webRTC/janus/03-attach/
/blog/answer/webRTC/janus/
/blog/code/react/redux/
/blog/code/react/router/01-环境设置/
/blog/answer/webRTC/janus/02-janus/
/blog/code/react/router/
/blog/know/back/Egg/01-基础/
/blog/know/back/Egg/egg兼容mysql和mogodb/
/blog/know/back/Egg/
/blog/know/back/nodejs/01-install/
/blog/know/back/nodejs/98-process/
/blog/know/back/nodejs/99-file/
/blog/know/back/nodejs/
/blog/know/computer/algorithm/01-多选投票算法/
/blog/know/computer/algorithm/02-二叉树的各种遍历/
/blog/know/computer/algorithm/03-位运算/
/blog/know/computer/algorithm/04-距离相关/
/blog/know/computer/algorithm/06-线性表/
/blog/know/computer/algorithm/05-字符串/
/blog/know/computer/algorithm/07-队列/
/blog/know/computer/algorithm/08-栈/
/blog/know/computer/algorithm/09-哈希表/
/blog/know/computer/algorithm/10-dfs/
/blog/know/computer/algorithm/11-bfs/
/blog/know/computer/algorithm/
/blog/know/computer/data/01-队列/
/blog/know/computer/data/03-链表/
/blog/know/computer/data/04-树/
/blog/know/computer/data/05-栈/
/blog/know/computer/data/06-其他/
/blog/know/computer/data/06-堆/
/blog/know/computer/data/
/blog/know/computer/dayOne/1011. 在 D 天内送达包裹的能力/
/blog/know/computer/dayOne/1310. 子数组异或查询/
/blog/know/computer/dayOne/137. 只出现一次的数字 II/
/blog/know/computer/dayOne/1473. 粉刷房子 III/
/blog/know/computer/dayOne/1482. 制作 m 束花所需的最少天数/
/blog/know/computer/dayOne/1486. 数组异或操作/
/blog/know/computer/dayOne/1720. 解码异或后的数组/
/blog/know/computer/dayOne/1723. 完成所有工作的最短时间/
/blog/know/computer/dayOne/1734. 解码异或后的排列/
/blog/know/computer/dayOne/554. 砖墙/
/blog/know/computer/dayOne/633. 平方数之和/
/blog/know/computer/dayOne/690. 员工的重要性/
/blog/know/computer/dayOne/7. 整数反转/
/blog/know/computer/dayOne/403. 青蛙过河/
/blog/know/computer/dayOne/740. 删除并获得点数/
/blog/know/computer/dayOne/872. 叶子相似的树/
/blog/know/computer/dayOne/938. 二叉搜索树的范围和/
/blog/know/computer/dayOne/
/blog/know/computer/network/01-网路协议/
/blog/know/computer/network/98-关于options请求/
/blog/know/computer/network/
/blog/know/engineering/babel/
/blog/know/engineering/npm/78-npm push/
/blog/know/engineering/npm/
/blog/know/engineering/react/01-简介/
/blog/know/engineering/react/03-react-router/
/blog/know/engineering/react/04-hooks/
/blog/know/engineering/react/
/blog/know/engineering/webpack/01-基础/
/blog/know/engineering/webpack/02-loader/
/blog/know/engineering/webpack/03-plugin/
/blog/know/engineering/webpack/
/blog/know/front/css/01-选择器/
/blog/know/front/css/02-盒模型/
/blog/know/front/css/03-布局/
/blog/know/front/css/04-文本属性/
/blog/know/front/css/
/blog/know/front/network/01-网路/
/blog/know/front/network/
/blog/know/front/html/01-head/
/blog/know/front/html/02-body/
/blog/know/front/html/09-canvas/
/blog/know/front/html/10-svg/
/blog/know/front/html/
/blog/know/front/react/01-xx/
/blog/know/front/react/02-props/
/blog/know/front/react/03-state/
/blog/know/front/react/04-Lifecycle/
/blog/know/front/react/05-hook/
/blog/know/front/react/06-redux/
/blog/know/front/react/
/blog/know/front/webRTC/
/blog/know/front/webpack/01-基础/
/blog/know/front/webpack/02-loader/
/blog/know/front/webpack/03-plugin/
/blog/know/front/webpack/
/blog/know/javascript/BOM和DOM/01-navigator/
/blog/know/javascript/BOM和DOM/
/blog/know/javascript/advance/07-迭代器与生成器/
/blog/know/javascript/advance/11-promise/
/blog/know/javascript/advance/12-正则表达式/
/blog/know/javascript/advance/
/blog/know/javascript/basic/02-类/
/blog/know/javascript/basic/03-数据类型/
/blog/know/javascript/basic/04-函数进阶/
/blog/know/javascript/basic/
/blog/know/javascript/api/99-promise/
/blog/know/javascript/basic/01-语言基础/
/blog/know/javascript/basic/05-原型链/
/blog/know/tool/chromedevtools/
/blog/know/tool/git/01-基础/
/blog/know/tool/git/02-log/
/blog/know/tool/git/03-checkout/
/blog/know/tool/git/09-submodule/
/blog/know/tool/git/04-diff/
/blog/know/tool/git/10-workflow/
/blog/know/tool/git/
/blog/know/tool/vscode/
/blog/know/typescript/basic/01-基础概念/
/blog/know/typescript/basic/02-基础类型/
/blog/know/typescript/basic/03-接口/
/blog/know/typescript/basic/04-类/
/blog/know/typescript/basic/05-函数/
/blog/know/typescript/basic/06-泛型/
/blog/know/typescript/basic/07-枚举/
/blog/know/typescript/basic/08-高级类型/
/blog/know/typescript/basic/09-模块/
/blog/know/typescript/basic/10-模块解析/
/blog/know/typescript/basic/11-命名空间/
/blog/know/typescript/basic/
/blog/know/typescript/declarationfiles/01-示例/
/blog/know/typescript/declarationfiles/02-结构/
/blog/know/typescript/declarationfiles/03-模板/
/blog/know/typescript/declarationfiles/04-最佳实践/
/blog/know/typescript/declarationfiles/05-深入/
/blog/know/typescript/declarationfiles/

代码库结构

一般来讲,组织声明文件的方式取决于代码库是如何被使用的。 在 JavaScript 中一个代码库有很多使用方式,这就需要你书写声明文件去匹配它们。 这篇指南涵盖了如何识别常见代码库的模式,以及怎样书写符合相应模式的声明文件。

针对代码库的每种主要的组织模式,在模版一节都有对应的文件。 你可以利用它们帮助你快速上手。

识别代码库的类型

首先,我们先看一下 TypeScript 声明文件能够表示的库的类型。 这里会简单展示每种类型的代码库的使用方式,以及如何去书写,还有一些真实案例。

识别代码库的类型是书写声明文件的第一步。 我们将会给出一些提示,关于怎样通过代码库的使用方法及其源码来识别库的类型。 根据库的文档及组织结构的不同,在这两种方式中可能一个会比另外的一个简单一些。 我们推荐你使用任意你喜欢的方式。

你应该寻找什么?

在为代码库编写声明文件时,你需要问自己以下几个问题。

  1. 如何获取代码库?

比如,是否只能够从 npm 或 CDN 获取。

  1. 如何导入代码库?

它是否添加了某个全局对象?它是否使用了 requireimport / export 语句?

针对不同类型的代码库的示例

模块化代码库

几乎所有的 Node.js 代码库都属于这一类。 这类代码库只能工作在有模块加载器的环境下。 比如, express 只能在 Node.js 里工作,所以必须使用 CommonJSrequire 函数加载。

ECMAScript 2015(也就是 ES2015,ECMAScript 6 或 ES6),CommonJSRequireJS 具有相似的导入一个模块的写法。 例如,对于 JavaScript CommonJS (Node.js),写法如下:

var fs = require('fs');

对于 TypeScript 或 ES6, import 关键字也具有相同的作用:

import * as fs from 'fs';

你通常会在模块化代码库的文档里看到如下说明:

var someLib = require('someLib');

define(..., ['someLib'], function(someLib) {

});

与全局模块一样,你也可能会在 UMD 模块的文档里看到这些例子,因此要仔细查看源码和文档。

从代码上识别模块化代码库

模块化代码库至少会包含以下代表性条目之一:

  • 无条件的调用 requiredefine
  • import * as a from 'b';export c; 这样的声明
  • 赋值给 exportsmodule.exports

它们极少包含:

  • windowglobal 的赋值

有以下四个模版可用:

  • module.d.ts
  • module-class.d.ts
  • module-function.d.ts
  • module-plugin.d.ts

你应该先阅读 module.d.ts 以便从整体上了解它们的工作方式。

然后,若一个模块可以当作函数调用,则使用 module-function.d.ts

const x = require('foo');
// Note: calling 'x' as a function
const y = x(42);

如果一个模块可以使用 new 来构造,则使用module-class.d.ts

var x = require('bar');
// Note: using 'new' operator on the imported variable
var y = new x('hello');

如果一个模块在导入后会更改其它的模块,则使用module-plugin.d.ts

const jest = require('jest');
require('jest-matchers-files');

全局代码库

全局代码库可以通过全局作用域来访问(例如,不使用任何形式的 import 语句)。 许多代码库只是简单地导出一个或多个供使用的全局变量。 比如,如果你使用 jQuery,那么可以使用 $ 变量来引用它。

$(() => {
    console.log('hello!');
});

你通常能够在文档里看到如何在 HTML 的 script 标签里引用代码库:

<script src="http://a.great.cdn.for/someLib.js"></script>

目前,大多数流行的全局代码库都以 UMD 代码库发布。 UMD 代码库与全局代码库很难通过文档来识别。 在编写全局代码库的声明文件之前,确保代码库不是 UMD 代码库。

从代码来识别全局代码库

通常,全局代码库的代码十分简单。 一个全局的 “Hello, world” 代码库可以如下

function createGreeting(s) {
    return 'Hello, ' + s;
}

或者这样:

window.createGreeting = function (s) {
    return 'Hello, ' + s;
};

在阅读全局代码库的代码时,你会看到:

  • 顶层的 var 语句或 function 声明
  • 一个或多个 window.someName 赋值语句
  • 假设 DOM 相关的原始值 documentwindow 存在

你不会看到:

  • 检查或使用了模块加载器,如 requiredefine
  • CommonJS/Node.js 风格的导入语句,如 var fs = require("fs");
  • define(...) 调用
  • 描述 require 或导入代码库的文档

全局代码库的示例

由于将全局代码库转换为 UMD 代码库十分容易,因此很少有代码库仍然使用全局代码库风格。 然而,小型的代码库以及需要使用 DOM 的代码库仍然可以是全局的。

全局代码库的模版

模版文件 global.d.ts 定义了 myLib 示例代码库。 请务必阅读脚注:"防止命名冲突"。

UMD

一个 UMD 模块既可以用作 ES 模块(使用导入语句),也可以用作全局变量(在缺少模块加载器的环境中使用)。 许多流行的代码库,如 Moment.js,都是使用这模式发布的。 例如,在 Node.js 中或使用了 RequireJS 时,你可以这样使用:

import moment = require('moment');
console.log(moment.format());

在纯浏览器环境中,你可以这样使用:

console.log(moment.format());

识别 UMD 代码库

UMD 模块 会检查运行环境中是否存在模块加载器。 这是一种常见模式,示例如下:

(function (root, factory) {
    if (typeof define === "function" && define.amd) {
        define(["libName"], factory);
    } else if (typeof module === "object" && module.exports) {
        module.exports = factory(require("libName"));
    } else {
        root.returnExports = factory(root.libName);
    }
}(this, function (b) {

如果你看到代码库中存在类如 typeof definetypeof windowtypeof module 的检测代码,尤其是在文件的顶端,那么它大概率是 UMD 代码库。

在 UMD 模块的文档中经常会提供在 Node.js 中结合 require 使用的示例,以及在浏览器中结合 <script> 标签使用的示例。

UMD 代码库的示例

大多数流行的代码库均提供了 UMD 格式的包。 例如,jQueryMoment.jslodash等。

模版

使用 module-plugin.d.ts 模版。

全局插件

一个全局插件是全局代码,它们会改变全局对象的结构。 对于全局修改的模块,在运行时存在冲突的可能。

比如,一些库往 Array.prototypeString.prototype 里添加新的方法。

识别全局插件

全局通常很容易地从它们的文档识别出来。

你会看到像下面这样的例子:

var x = 'hello, world';
// Creates new methods on built-in types
console.log(x.startsWithHello());

var y = [1, 2, 3];
// Creates new methods on built-in types
console.log(y.reverseAndSort());

模版

使用 global-plugin.d.ts 模版。

全局修改的模块

当一个全局修改的模块被导入的时候,它们会改变全局作用域里的值。 比如,存在一些库它们添加新的成员到 String.prototype 当导入它们的时候。 这种模式很危险,因为可能造成运行时的冲突, 但是我们仍然可以为它们书写声明文件。

识别全局修改的模块

全局修改的模块通常可以很容易地从它们的文档识别出来。 通常来讲,它们与全局插件相似,但是需要 require 调用来激活它们的效果。

你可能会看到像下面这样的文档:

// 'require' call that doesn't use its return value
var unused = require('magic-string-time');
/* or */
require('magic-string-time');

var x = 'hello, world';
// Creates new methods on built-in types
console.log(x.startsWithHello());

var y = [1, 2, 3];
// Creates new methods on built-in types
console.log(y.reverseAndSort());

模版

使用 global-modifying-module.d.ts 模版。

利用依赖

你的代码库可能会有若干种依赖。 本节会介绍如何在声明文件中导入它们。

对全局库的依赖

如果你的代码库依赖于某个全局代码库,则使用 /// <reference types="..." /> 指令:`

/// <reference types="someLib" />

function getThing(): someLib.thing;

对模块的依赖

如果你的代码库依赖于某个模块,则使用 import 语句:

import * as moment from 'moment';

function getThing(): moment;

对 UMD 模块的依赖

全局代码库

如果你的全局代码库依赖于某个 UMD 模块,则使用 /// <reference types 指令:

/// <reference types="moment" />

function getThing(): moment;

ES 模块或 UMD 模块代码库

如果你的模块或 UMD 代码库依赖于某个 UMD 代码库,则使用 import 语句:

import * as someLib from 'someLib';

不要使用 /// <reference 指令来声明对 UMD 代码库的依赖。

脚注

防止命名冲突

注意,虽说可以在全局作用域内定义许多类型。 但我们强烈建议不要这样做,因为当一个工程中存在多个声明文件时,它可能会导致难以解决的命名冲突。

可以遵循的一个简单规则是使用代码库提供的某个全局变量来声明拥有命名空间的类型。 例如,如果代码库提供了全局变量 cats ,那么可以这样写:

declare namespace cats {
    interface KittySettings {}
}

而不是:

// at top-level
interface CatsKittySettings {}

这样做会保证代码库可以被转换成 UMD 模块,且不会影响声明文件的使用者。

ES6 对模块插件的影响

一些插件会对已有模块的顶层导出进行添加或修改。 这在 CommonJS 以及其它模块加载器里是合法的,但 ES6 模块是不可改变的,因此该模式是不可行的。 因为,TypeScript 是模块加载器无关的,所以在编译时不会对该行为加以限制,但是开发者若想要转换到 ES6 模块加载器则需要注意这一点。

ES6 对模块调用签名的影响

许多代码库,如 Express,将自身导出为可调用的函数。 例如,Express 的典型用法如下:

import exp = require('express');
var app = exp();

在 ES6 模块加载器中,顶层对象(此例中就 exp )只能拥有属性; 顶层的模块对象永远不能够被调用。

最常见的解决方案是为可调用的 / 可构造的对象定义一个 default 导出; 有些模块加载器会自动检测这种情况并且将顶层对象替换为 default 导出。 如果在 tsconfig.json 里启用了"esModuleInterop": true,那么 Typescript 会自动为你处理。