声明文件
TypeScript 作为 JavaScript 的超集,在开发过程中不可避免要引用其他第三方的 JavaScript 的库。虽然通过直接引用可以调用库的类和方法,但是却无法使用TypeScript 诸如类型检查等特性功能。通过引用这个声明文件,就可以借用 TypeScript 的各种特性来使用库文件了
使用 非typescript 库, 需要 引入自定义声明文件暴露接口才可以使用, 在ts 文件里头部 /// <reference path=”…” />, 声明文件间也可以用/// <reference path=”…” /> 互相引用;
.ts 文件 经过编译 会生成.d.ts,可以通过编译选项开关
全局库
模版文件global.d.ts
定义了myLib库作为例子。 一定要阅读 “防止命名冲突”补充说明。
模块化库
针对模块有三种可用的模块, module.d.ts
, module-class.d.ts
and module-function.d.ts
.
- 使用
module-function.d.ts
,如果模块能够作为函数调用。 - 使用
module-class.d.ts
,如果模块能够使用new
来构造: - 如果模块不能被调用或构造,使用
module.d.ts
文件。
使用module-function.d.ts 模块,因为 模块可以作为函数调用
var x = require("foo"); //
// Note: calling 'x' as a function
var y = x(42);
declare 关键字
作用:告诉编译器,某个项(对象,变量,包,类等等)已经存在了,不用检查
三斜线指令
三斜线指令是包含单个XML标签的单行注释。 注释的内容会做为编译器指令使用。
/// <reference path=”…” />指令是三斜线指令中最常见的一种。 它用于引入声明文件。三斜线引用告诉编译器在编译过程中要引入的额外的文件。
///<reference types=”…” />与 /// <reference path=”…” />指令相似,这个指令是用来声明 依赖的。一个 /// <reference types=”…” />指令则声明了对某个包的依赖。可以简单地把三斜线类型引用指令当做 import声明的包。
例如,把 /// <reference types=”node” />引入到声明文件,表明这个文件使用了@types/node/index.d.ts里面声明的名字; 并且,这个包需要在编译阶段与声明文件一起被包含进来。仅当在你需要写一个d.ts文件时才使用这个指令。
使用依赖
依赖全局库
如果你的库依赖于某个全局库,使用 /// <reference types="..." />
指令:
/// <reference types="someLib" />
function getThing(): someLib.thing;
依赖模块
如果你的库依赖于模块,使用import
语句:
import * as moment from "moment";
function getThing(): moment;
依赖UMD库 分两种情况
- 从全局库 / 如果你的全局库依赖于某个UMD模块,使用/// <reference types>指令
- 从一个模块或UMD库 / 如果你的模块或UMD库依赖于一个UMD库,使用
import
语句
命名空间和模块的陷阱
对模块使用
要想描述非TypeScript编写的类库的类型,我们需要声明类库所暴露出的API。
外部模块
在Node.js里大部分工作是通过加载一个或多个模块实现的。 我们可以使用顶级的 export
声明来为每个模块都定义一个.d.ts
文件,但最好还是写在一个大的.d.ts
文件里。 我们使用与构造一个外部命名空间相似的方法,但是这里使用 module
关键字并且把名字用引号括起来,方便之后import
// node.d.ts 文件
declare module "url" {
export function normalize(p: string): string;
export function join(...paths: any[]): string;
export let sep: string;
}
现在我们可以/// <reference> node.d.ts
并且使用import url = require("url");
或import * as URL from "url"
加载模块。
/// <reference path="node.d.ts"/>
import * as URL from "url";
let myUrl = URL.parse("http://www.typescriptlang.org");
外部模块简写 / 假如你不想在使用一个新模块之前花时间去编写声明,你可以采用声明的简写形式以便能够快速使用它。
//声明文件
declare module "hot-new-module";
//简写模块里所有导出的类型将是any。
//其他文件 模块引入
import x, {y} from "hot-new-module";
x(y);
一个常见的错误是使用/// <reference>引用模块文件,应该使用import。要理解这之间的区别,我们首先应该弄清编译器是如何根据 import路径(例如,import x from “…”)里面的…,等等)来定位模块的类型信息的。
编译器首先尝试去查找相应路径下的 .ts,.tsx再或者.d.ts。 如果这些文件都找不到,编译器会查找 外部模块声明。 回想一下,它们是在 .d.ts文件里声明的。
myModules.d.ts
// In a .d.ts file or .ts file that is not a module:
declare module "SomeModule" {
export function fn(): string;
}
myOtherModule.ts
/// <reference path="myModules.d.ts" />
// 这里的引用标签指定了外来模块的位置
import * as m from "SomeModule";
模块解析策略
共有两种可用的模块解析策略:Node和Classic。 你可以使用 --moduleResolution
标记来指定使用哪种模块解析策略。若未指定,那么在使用了 --module AMD | System | ES2015
时的默认值为Classic,其它情况时则为Node。
有一个对moduleB
的非相对导入import { b } from “moduleB”,它是在/root/src/folder/A.ts
文件里,会以如下的方式来定位"moduleB"
:
- /root/src/folder/moduleB.ts
- /root/src/folder/moduleB.d.ts
- /root/src/moduleB.ts
- /root/src/moduleB.d.ts
- /root/moduleB.ts
- /root/moduleB.d.ts
- /moduleB.ts
- /moduleB.d.ts
Node.js如何解析模块
为了理解TypeScript编译依照的解析步骤,先弄明白Node.js模块是非常重要的。 通常,在Node.js里导入是通过 require函数调用进行的。 Node.js会根据 require的是相对路径还是非相对路径做出不同的行为。
相对路径很简单。 例如,假设有一个文件路径为 /root/src/moduleA.js,包含了一个导入var x = require(“./moduleB”); Node.js以下面的顺序解析这个导入:
- 检查/root/src/moduleB.js文件是否存在。
- 检查
/root/src/moduleB
目录是否包含一个package.json
文件,且package.json
文件指定了一个"main"
模块。 在我们的例子里,如果Node.js发现文件/root/src/moduleB/package.json
包含了{ "main": "lib/mainModule.js" }
,那么Node.js会引用/root/src/moduleB/lib/mainModule.js
。 - 检查
/root/src/moduleB
目录是否包含一个index.js
文件。 这个文件会被隐式地当作那个文件夹下的”main”模块。
还是用上面例子,但假设/root/src/moduleA.js
里使用的是非相对路径导入var x = require("moduleB");
。 Node则会以下面的顺序去解析 moduleB
,直到有一个匹配上。
/root/src/node_modules/moduleB.js
/root/src/node_modules/moduleB/package.json
(如果指定了"main"
属性)/root/src/node_modules/moduleB/index.js
/root/node_modules/moduleB.js
/root/node_modules/moduleB/package.json
(如果指定了"main"
属性)/root/node_modules/moduleB/index.js
/node_modules/moduleB.js
/node_modules/moduleB/package.json
(如果指定了"main"
属性)/node_modules/moduleB/index.js
TypeScript如何解析模块
TypeScript是模仿Node.js运行时的解析策略来在编译阶段定位模块定义文件。 因此,TypeScript在Node解析逻辑基础上增加了TypeScript源文件的扩展名( .ts
,.tsx
和.d.ts
)。 同时,TypeScript在 package.json
里使用字段"types"
来表示类似"main"
的意义 – 编译器会使用它来找到要使用的”main”定义文件。