Typescript declare , 命名空间和模块

声明文件

TypeScript 作为 JavaScript 的超集,在开发过程中不可避免要引用其他第三方的 JavaScript 的库。虽然通过直接引用可以调用库的类和方法,但是却无法使用TypeScript 诸如类型检查等特性功能。通过引用这个声明文件,就可以借用 TypeScript 的各种特性来使用库文件了

使用 非typescript 库, 需要 引入自定义声明文件暴露接口才可以使用, 在ts 文件里头部 /// <reference path=”…” />, 声明文件间也可以用/// <reference path=”…” /> 互相引用;

.ts 文件 经过编译 会生成.d.ts,可以通过编译选项开关

全局库

模版文件global.d.ts定义了myLib库作为例子。 一定要阅读 “防止命名冲突”补充说明

模块化库

针对模块有三种可用的模块, module.d.tsmodule-class.d.ts and module-function.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";

模块解析策略

共有两种可用的模块解析策略:NodeClassic。 你可以使用 --moduleResolution标记来指定使用哪种模块解析策略。若未指定,那么在使用了 --module AMD | System | ES2015时的默认值为Classic,其它情况时则为Node

有一个对moduleB的非相对导入import { b } from “moduleB”,它是在/root/src/folder/A.ts文件里,会以如下的方式来定位"moduleB"

  1. /root/src/folder/moduleB.ts
  2. /root/src/folder/moduleB.d.ts
  3. /root/src/moduleB.ts
  4. /root/src/moduleB.d.ts
  5. /root/moduleB.ts
  6. /root/moduleB.d.ts
  7. /moduleB.ts
  8. /moduleB.d.ts

Node.js如何解析模块

为了理解TypeScript编译依照的解析步骤,先弄明白Node.js模块是非常重要的。 通常,在Node.js里导入是通过 require函数调用进行的。 Node.js会根据 require的是相对路径还是非相对路径做出不同的行为。

相对路径很简单。 例如,假设有一个文件路径为 /root/src/moduleA.js,包含了一个导入var x = require(“./moduleB”); Node.js以下面的顺序解析这个导入:

  1. 检查/root/src/moduleB.js文件是否存在。
  2. 检查/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
  3. 检查/root/src/moduleB目录是否包含一个index.js文件。 这个文件会被隐式地当作那个文件夹下的”main”模块。

还是用上面例子,但假设/root/src/moduleA.js里使用的是非相对路径导入var x = require("moduleB");。 Node则会以下面的顺序去解析 moduleB,直到有一个匹配上。

  1. /root/src/node_modules/moduleB.js
  2. /root/src/node_modules/moduleB/package.json (如果指定了"main"属性)
  3. /root/src/node_modules/moduleB/index.js
  4. /root/node_modules/moduleB.js
  5. /root/node_modules/moduleB/package.json (如果指定了"main"属性)
  6. /root/node_modules/moduleB/index.js
  7. /node_modules/moduleB.js
  8. /node_modules/moduleB/package.json (如果指定了"main"属性)
  9. /node_modules/moduleB/index.js

TypeScript如何解析模块

TypeScript是模仿Node.js运行时的解析策略来在编译阶段定位模块定义文件。 因此,TypeScript在Node解析逻辑基础上增加了TypeScript源文件的扩展名( .ts.tsx.d.ts)。 同时,TypeScript在 package.json里使用字段"types"来表示类似"main"的意义 – 编译器会使用它来找到要使用的”main”定义文件。

Leave a Reply

Your email address will not be published. Required fields are marked *