TypeScript 系列学习笔记:
TypeScript | 第一章:环境搭建及基础数据类型
TypeScript | 第二章:类、接口和之间的关系
TypeScript | 第三章:函数、泛型和枚举
TypeScript | 第五章:高级类型
TypeScript | 第六章:理解声明合并,以及编写声明文件
TypeScript | 第七章:配置文件说明
一、命名空间
命名空间主要用于当代码庞大时,可以对函数/类/接口等进行分组管理。
命名空间是位于全局命名空间下的一个普通的带有名字的 JavaScript 对象。
容易污染全局命名空间,且难以识别组件之间的依赖关系。
1. 命名空间的使用
namespace Space{ // namespace:声明命名空间
const name:string = 'space'
// export:暴露给外部访问
export class Test{
name:string
}
// 命名空间也可用类型
export interface Man{
name:string
}
export function getName(){
console.log('space')
}
}
// 使用:
Space.name // 报错:Space上没有name属性
Space.getName() // 输出:'space'
let t:Space.Test
复制代码
我们将如上 ts 文件编译成 js 文件,代码如下:
var Space;
(function (Space) {
var name = 'space';
var Test = /** @class */ (function () {
function Test() {
}
return Test;
}());
Space.Test = Test;
function getName() {
console.log('space');
}
Space.getName = getName;
})(Space || (Space = {}));
复制代码
可以看出命名空间被编译成了一个全局对象,因此程序中需要暴露成全局对象,可以使用namespace
。
2. 命名空间的拆分和引用
命名空间可以分割为多个文件,尽管是不同文件,它们仍是同一个命名空间。
不同文件直接的依赖关系,可以通过引用标签来告诉编译器文件之间的关联。
// dateTool.ts
namespace Tool{
export function getDate(){
console.log('getDate')
}
}
复制代码
// timeTool.ts
namespace Tool{
export function getTime(){
console.log('getTime')
}
}
复制代码
/// <reference path="dateTool.ts" /> // 引用标签,引用命名空间
/// <reference path="timeTool.ts" />
Tool.getDate()
复制代码
如上代码编译后:
var Tool;
(function (Tool) {
function getDate() {
console.log('getDate');
}
Tool.getDate = getDate;
})(Tool || (Tool = {}));
var Tool;
(function (Tool) {
function getTime() {
console.log('getTime');
}
Tool.getTime = getTime;
})(Tool || (Tool = {}));
/// <reference path="names.ts" />
/// <reference path="names1.ts" />
Tool.getDate();
复制代码
编译后的代码可以看出,虽然命名空间进行了拆分,但最终仍然是共用一个命名空间。
3. export=
如果在tsconfig
中设置了"module": "umd"
,那么export =
Tool
等价于export default Tool
,export=
常见于支持 umd 的插件的声明文件。
4. 命名空间在 D3 的使用
D3 在全局对象d3
里定义它的功能。 因为这个库通过一个 <script>
标签加载(不是通过模块加载器),它的声明文件使用内部模块来定义它的类型。 为了让 TypeScript 编译器识别它的类型,使用外部命名空间声明。
declare namespace D3 {
export interface Selectors {
select: {
(selector: string): Selection;
(element: EventTarget): Selection;
};
}
export interface Event {
x: number;
y: number;
}
export interface Base extends Selectors {
event: Event;
}
}
declare var d3: D3.Base;
复制代码
二、模块
TypeScript 与 ES5 一样,任何包含顶级import
或者export
的文件都被当成一个模块,相反的则被视为全局可见,当然对模块也可见。
模块是自声明的,两模块间的关系是通过文件级别上的imports
和exports
建立。
模块使用模块加载器去导入其它的模块。 在运行时,模块加载器的作用是在执行此模块代码前去查找并执行这个模块的所有依赖。熟知的 JavaScript 模块加载器有服务于Node.js
的 CommonJS
和服务于 Web 应用的Require.js
1. 导出
const num = 1;
// 导出
export {num}
// 导出重命名
export {num as count}
// 重新导出,并重命名
export {a as Al} from 'xx'
// 重新导出所有模块
export * from 'xx'
// 默认导出
export default $
复制代码
2. 导入
// 导入
import {num} from 'xx'
// 导入模块的某部分重新命名
import {num as Count} from 'xx'
// 导出整个模块到一个变量
import * as Data from 'xx'
复制代码
3. export= 和 import = require()
ES6 的export default
语法并不能兼容 CommonJS 和 AMD 的exports
。为了支持 CommonJS 和 AMD 的exports
, TypeScript 提供了export =
语法。
export =
语法定义一个模块的导出对象
。
若使用export =
导出一个模块,则必须使用 TypeScript 的特定语法import module = require("module")
来导入此模块。
// 定义导出模块Tool
class Tool{
getDate(){}
}
export = Tool
// 导入Tool模块
import Tool = require('./Tool')
new Tool().getDate()
复制代码
4. 模块的动态加载
typeof
关键字,当在表示类型的地方使用时,会得出一个类型值,这里就表示模块的类型。
这里展示下 Node.js 里的动态模块加载。
declare function require(moduleName: string): any;
import { ZipCodeValidator as Zip } from "./ZipCodeValidator";
if (needZipValidation) {
let ZipCodeValidator: typeof Zip = require("./ZipCodeValidator");
let validator = new ZipCodeValidator();
if (validator.isAcceptable("...")) { /* ... */ }
}
复制代码
三、总结
至此我们完成了 TypeScript 里的命名空间和模块的学习。
评论