https://www.tslang.cn/docs/handbook/namespaces.html
内部模块
“内部模块”现在叫做“命名空间”。 另外,任何使用 module关键字来声明一个内部模块的地方都应该使用namespace关键字来替换。 这就避免了让新的使用者被相似的名称所迷惑。
- namespace / module 一般称之为内部模块。现在有了更方便的import / export,这个就不怎么用了。
- 外部模块 import / export。
- 在代码量较大的情况下,为了避免命名空间冲突,可以将相似的函数、类、接口放置到命名空间内
- 命名空间可以将代码包裹起来,只对外暴露需要在外部访问的对象,命名空间内通过
export向外导出 - 命名空间是内部模块,主要用于组织代码,避免命名冲突
- 两个重名的命名空间会合并, 但是如果里面有重名的属性会报错。
- 命名空间可以嵌套命名空间。
原理
其实一个命名空间本质上是一个对象,它的作用是将一系列相关的全局变量组织到一个对象的属性。
namespace Numbers {export let a = 1;export let b = 2;}// 编译为var Numbers;(function (Numbers) {Numbers.a = 1;Numbers.b = 2;})(Numbers || (Numbers = {}));
内部划分
- 不加export,namespace是在全局模块中划分空间。
- 加上export之后,namespace就是划分的本模块的局部空间。
1.ts
export namespace x {export class Dog{eat(){ console.log('x-Dog-eat'); }}}export namespace y {export class Dog{eat(){ console.log('y-Dog-eat'); }}}let x_dog = new x.Dog();x_dog.eat(); //x-Dog-eatlet y_dog = new y.Dog();y_dog.eat(); //x-Dog-eat
2.ts
import { x, y } from './2'let x_dog = new x.Dog();x_dog.eat(); //x-Dog-eatlet y_dog = new y.Dog();y_dog.eat(); //x-Dog-eat
分离多文件
- 当应用变得越来越大时,可以将代码分离到不同的文件中以便于维护。
- 相同名字的namespace,由于处于一个命名空间,可以合并,并且在使用的时候就如同它们在一个文件中定义的一样。
别名
另一种简化命名空间操作的方法是使用import q = x.y.z给常用的对象起一个短的名字。 不要与用来加载模块的import x = require('name')语法弄混了,这里的语法是为指定的符号创建一个别名。 你可以用这种方法为任意标识符创建别名,也包括导入的模块中的对象。
namespace Shapes {export namespace Polygons {export class Triangle { }export class Square { }}}import polygons = Shapes.Polygons;let sq = new polygons.Square(); // Same as "new Shapes.Polygons.Square()"
注意,我们并没有使用require关键字,而是直接使用导入符号的限定名赋值。 这与使用 var相似,但它还适用于类型和导入的具有命名空间含义的符号。 重要的是,对于值来讲, import会生成与原始符号不同的引用,所以改变别名的var值并不会影响原始变量的值。
文件、模块、命名空间
文件和模块
每个 module都不一样。
src\table1.ts
export module Box{export class Book1{}}
src\table2.ts
export module Box{export class Book1{}}
src\table3.ts
export module Box{export class Book1{}}
module A{export namespace Box{export const a = 1;}}//如果想获取模块A里面的东西,A里面的东西,必须要加导出module B{export namespace Box{export const a = 1;console.log(A.Box.a); //1}}
空间
namespace 和 module 不一样,namespace 在全局空间中具有唯一性.
在不同的外部模块不会合并
module A{namespace Box{export class Book1{}}}module B{namespace Box{export class Book2{}}console.log(Box); //{ Book2: [Function: Book2] }}
在全局模块里会合并
//1.tsnamespace Box{export class Book{}}//2.tsnamespace Box{export class Book{} //error,“Book” 重复}
文件
每个文件是独立的。
src\table1.ts
export class Book1 { }
src\table2.ts
export class Book1 { }
src\table3.ts
export class Book1 { }
外部模块
- ESModule: export import 语法
- Commonjs: require module.exports 语法
- rollup 默认只支持es6语法,只有es6模块才能做tree-shaking ```typescript //2.ts const str:string = ‘jack’; module.exports = str;
//1.ts // import two from ‘./2’; //rollup不支持 let two = require(‘./2’); //commonjs语法的require语法是没有类型提示的
es6 模块和commonjs模块不兼容,有可能别人的包是commonjs打包出的结果,es6引入就会出问题,所以 ts 为了支持commonjs语法,单独的提出了一个导出方式。`export = str` / `import two = require()````typescript//node下用commonjs写ts的话//2.tsconst str:string = 'jack';export = str;//1.ts// let two = require('./2'); //这种不行,无类型提示// 用ts的时候,除非你引入的模块,它不是ts写的,我们可以使用require直接用// 如果模块用ts写的,那就需要用 import x = requie(''),这样就会有提示import two = require('./2');console.log(two);
如果要是es6模块,全部用 export、export default、export {} / import 即可
//2.tsconst str:string = 'jack';export default str;//1.tsimport two from './2';console.log(two);
使用其它的JavaScript库
为了描述不是用TypeScript编写的类库的类型,我们需要声明类库导出的API。 由于大部分程序库只提供少数的顶级对象,命名空间是用来表示它们的一个好办法。
我们称其为声明是因为它不是外部程序的具体实现。 我们通常在 .d.ts里写这些声明。 如果你熟悉C/C++,你可以把它们当做 .h文件。 让我们看一些例子。
外部命名空间
流行的程序库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;
