Library Structures
库结构
概观
一般来说,您构建
声明文件的方式取决于库的使用方式。在JavaScript中提供用于消费的库的方法很多,您需要编写您的声明文件以匹配它。本指南介绍了如何识别公共库模式,以及如何编写与该模式相对应的声明文件。
每种主要的图书馆结构模式都在模板部分有相应的文件。您可以从这些模板开始,帮助您更快地完成工作。
识别各种图书馆
首先,我们将回顾TypeScript声明文件可以表示的类库。我们将说明如何每种库时使用
,它是如何写的
,而在现实世界中列举了一些例子库。
识别库的结构是编写其声明文件的第一步。我们将提示如何根据用法
和代码
来识别结构。根据图书馆的文件和组织,一个可能比另一个更容易。我们建议您选择使用更舒适的产品。
全局图书馆
甲全球
库是一个可以从全球
范围内被访问(即不使用任何形式的import
)。许多库只是公开一个或多个全局变量供使用。例如,如果您使用jQuery,则$
可以通过简单引用它来使用该变量:
$(() => { console.log('hello!' }
您通常会在全局库的文档中看到如何在HTML脚本标记中使用库的指导:
<script src="http://a.great.cdn.for/someLib.js"></script>
今天,大多数流行的全球可访问的库实际上被编写为UMD库(见下文)。UMD库文档很难与全局库文档区分开来。在编写全局声明文件之前,请确保该库实际上不是UMD。
从代码中识别全局库
全球图书馆代码通常非常简单。一个全球性的“你好,世界”图书馆可能是这样的:
function createGreeting(s) {
return "Hello, " + s;
}
或者像这样:
window.createGreeting = function(s) {
return "Hello, " + s;
}
在查看全球图书馆的代码时,您通常会看到:
- 顶级
var
语句或function
声明
- 一个或多个任务
window.someName
- DOM基元喜欢
document
或window
存在的假设
你不会
看到:
- 检查或使用模块加载程序,例如
require
或define
- CommonJS/Node.js样式的表单导入
var fs = require("fs"
- 调用
define(...)
- 描述如何
require
或导入库的文档
全局图书馆的例子
由于将全局库变成UMD库通常很容易,因此很少有流行库仍以全局风格编写。然而,那些小而需要DOM(或者没有
依赖)的库可能仍然是全局的。
全局图书馆模板
模板文件global.d.ts
定义了一个示例库myLib
。请务必阅读“防止名称冲突”脚注。
模块化图书馆
一些库只能在模块加载器环境中工作。例如,因为express
只能在Node.js中工作,并且必须使用CommonJS require
函数加载。
ECMAScript的2015(也称为ES2015,ECMAScript的6和ES6),CommonJS的,和RequireJS具有类似概念导入
一个模块
。例如,在JavaScript CommonJS(Node.js)中,你会写
var fs = require("fs"
在TypeScript或ES6中,import
关键字具有相同的用途:
import fs = require("fs"
您通常会看到模块化库在其文档中包含以下其中一行:
var someLib = require('someLib'
要么
define(..., ['someLib'], function(someLib) {
}
与全局模块一样,您可能会在UMD模块的文档中看到这些示例,因此请务必检查代码或文档。
从代码中识别模块库
模块化库通常至少具有以下一些内容:
- 无条件调用
require
或define
- 声明像
import * as a from 'b';
或export c;
- 分配给
exports
或module.exports
他们很少会有:
- 分配属性
window
或global
模块化库的例子
许多流行的Node.js库的模块系列,如express
,gulp
和request
。
UMD
甲UMD
模块是一个可以或者
被用作模块(通过一个进口),或作为一个全局(当在环境中时不带模块加载程序运行)。许多流行的库,如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 define
,typeof window
或typeof module
在库中的代码,尤其是在文件的顶部,它几乎总是一个UMD库。
UMD库的文档通常还会演示一个“Using in Node.js”示例require,以及一个“在浏览器中使用”示例,显示使用<script>标签加载脚本。
UMD库的例子
现在大多数流行的图书馆都可以作为UMD套餐。例子包括jQuery,Moment.js,lodash等等。
模板
有三个模板可用于模块module.d.ts
,module-class.d.ts
和module-function.d.ts
。
使用module-function.d.ts
,如果你的模块可以被称为
像函数:
var x = require("foo"
// Note: calling 'x' as a function
var y = x(42
请务必阅读脚注“ES6对模块调用签名的影响”
使用module-class.d.ts
,如果你的模块可以构建
使用new
:
var x = require("bar"
// Note: using 'new' operator on the imported variable
var y = new x("hello"
相同的脚注适用于这些模块。
如果您的模块不可调用或可构建,请使用该module.d.ts
文件。
模块插件或UMD插件
模块插件
改变另一模块(无论是UMD或模块)的形状。例如,在Moment.js中,为对象moment-range
添加了一个新range
方法moment
。
为了编写声明文件,无论被更改的模块是普通模块还是UMD模块,都将编写相同的代码。
模板
使用module-plugin.d.ts
模板。
全局插件
一个全局的插件
是全球性的代码,改变一些全局的形状。与全局修改模块一样
,这会增加运行时冲突的可能性。
例如,一些库添加新的功能Array.prototype
或String.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;
来自模块或UMD库
如果您的模块或UMD库依赖于UMD库,请使用以下import
语句:
import * as someLib from 'someLib';
千万不能使用/// <reference指令声明依赖于UMD库!
脚注
防止名称冲突
请注意,编写全局声明文件时,可以在全局范围中定义多种类型。我们强烈建议不要这样做,因为当许多声明文件在项目中时,会导致可能无法解析的名称冲突。
遵循的一条简单规则是只声明由库定义的任何全局变量的名称空间
类型。例如,如果库定义了全局值'猫',你应该写
declare namespace cats {
interface KittySettings { }
}
但不是
// at top-level
interface CatsKittySettings { }
该指导还确保了该库可以过渡到UMD而不会破坏声明文件用户。
ES6对模块插件的影响
一些插件添加或修改现有模块的顶级导出。虽然在CommonJS和其他装载器中这是合法的,但ES6模块被认为是不可变的,这种模式将不可能实现。因为TypeScript是与loader无关的,所以没有编译时执行这个策略,但是打算转换到ES6模块加载器的开发人员应该知道这一点。
ES6对模块呼叫签名的影响
许多流行的库,如Express,在导入时将自己公开为可调用的函数。例如,典型的Express用法如下所示:
import exp = require("express"
var app = exp(
在ES6模块加载器中,顶级对象(此处导入为exp
)只能具有属性; 顶级模块对象永远不可
调用。这里最常见的解决方案是default
为可调用/可构造对象定义一个导出; 某些模块加载器垫片会自动检测到这种情况,并用default
导出代替顶层对象。