Modules
从ECMAScript 2015开始,JavaScript有了模块的概念。在TypeScript中,也有相同的概念。
-
模块在自己的作用域执行,而不是在全局作用域;这意味着声明在一个模块中的变量、函数、类等对外不可见,除非显示使用
export导出。相反地,为了使用从另一个模块导出的变量、函数、类、接口等,需要使用import导入。 -
模块是声明性的,模块之间的关系在文件级别以导入和导出来指定。
-
模块使用模块加载器相互导入。在运行时,模块加载器负责在执行模块之前定位和执行模块的所有依赖项。JavaScript中使用的知名模块加载器是Node.js用于CommonJS模块的加载器和Web应用程序中AMD模块的RequireJS加载器。
在TypeScript中,就像在ECMAScript 2015中一样,任何包含顶级导入或导出的文件都被视为模块。相反,没有任何顶级导入或导出声明的文件被视为脚本,其内容在全局范围内可用(因此也适用于模块)。
Export
导出声明
任何声明可以通过export关键字导出:
// demo.ts
export interface StringValidator {
isAcceptable: (s: string) => boolean
}
// test.ts
import { StringValidator } from './demo.ts'
class Validator implements StringValidator {
isAcceptable(s: string) {
return typeof s === 'string'
}
}
export { Validator }
重新导出
通常模块会扩展其他模块,并部分地暴露一些功能。重新导出不会在本地导入它,也不会引入本地变量。
export { Validator as BaseValidator } from './test.ts'
export * from './test.ts'
Import
命名导入
从模块中导入内容可通过以下一种导入形式之一:
// 命名导入
import { StringValidator } from './demo.ts'
// 导入重命名
import { StringValidator as MyStringValidator } from './demo.ts'
// 全部导入
import * as MyValidator from './demo.ts'
还有一种导入模块以实现副作用的效果,虽然不建议这样做,但有些模块会设置一些全局状态,其他模块可以使用这些状态。这些模块可能没有任何导出,或者使用者对它们的导出不感兴趣。要导入这些模块,请使用以下方式:
import "./my-module.js";
导入类型
TypeScript 3.8及以上版本,可使用import或import type导入类型:
// api.ts
export interface APIResponseType {
status: number,
type: string,
data: object
}
export interface APIRequestType {
url: string,
method: string,
data: object
}
import { APIResponseType } from './api';
import type { APIRequestType } from './api';
import { APIRequestType, type APIResponseType} from './api';
任何明确标注的类型导入都将从JavaScript中被移除,而像Babel这样的工具可以通过 isolatedModules 编译器标记对你的代码进行更好的假设。
默认导出
每个模块可以选择性地导出一个默认导出。默认导出使用关键字 default 标记;每个模块只能有一个默认导出。默认导出使用不同的导入形式进行导入。
// jQuery.d.ts
declare let $: JQuery;
export default $;
// index.ts
import $ from "jquery";
$("button.continue").html("Next Step...");
类和函数声明可以直接作为默认导出,且默认导出的类和函数声明的名称是可选的:
// class example
export default class ZipCodeValidator {
static numberRegexp = /^[0-9]+$/;
isAcceptable(s: string) {
return s.length === 5 && ZipCodeValidator.numberRegexp.test(s);
}
}
import validator from "./ZipCodeValidator";
let myValidator = new validator();
// function example
export default function (s: string) {
return s.length === 5 && numberRegexp.test(s);
}
import validate from "./StaticZipCodeValidator";
当然,默认导出也可以只是值:
export default 'abc';
const obj = {};
export default obj;
export * as
TypeScript 3.8以上,可以使用 export * as ns 作为一种简写形式,将另一个模块重新导出并指定名称:
// index.ts
export * as utilities from "./utilities";
这将获取模块的所有依赖项,并将其作为导出字段,可以像这样导入它:
import { utilities } from "./index";
export = & import = require()
通常,CommonJS 和 AMD 都有一个包含模块所有导出的exports对象的概念。它们还支持使用自定义单个对象替换exports对象。默认导出旨在充当此行为的替代方式;然而,这两者是不兼容的。TypeScript 支持使用 export = 来模拟传统的 CommonJS 和 AMD 工作流程。
export = 语法指定了一个从模块导出的单个对象。该对象可以是类、接口、命名空间、函数或枚举。import = require()用来导入export = 导出的对象。
// api.ts
class API {}
export = API;
// index.ts
import API = require('./api');
环境模块
在Node.js中,大多数任务是通过加载一个或多个模块来完成的。我们可以将每个模块定义在自己的.d.ts文件中,使用顶级导出声明,但是将它们写成一个更大的.d.ts文件更加方便。为了实现这一点,我们使用类似于环境命名空间的结构,但是我们使用module关键字和被引入的模块的名称(用引号括起来),以便在后续的导入中使用。
declare module "url" {
export interface Url {
protocol?: string;
hostname?: string;
pathname?: string;
}
export function parse(
urlStr: string,
parseQueryString?,
slashesDenoteHost?
): Url;
}
declare module "path" {
export function normalize(p: string): string;
export function join(...paths: any[]): string;
export var sep: string;
}
可以使用import url = require("url");或者import * as URL from "url"加载以上模块:
/// <reference path="node.d.ts"/>
import * as URL from "url";
let myUrl = URL.parse("https://...");
简写形式
如果不想花时间在使用新模块之前编写声明,可以使用简化的声明来快速开始,但是从简化模块中导入的所有内容都将具有any类型:
declare module "hot-new-module";
import x, { y } from "hot-new-module";
x(y);