https://www.tslang.cn/docs/handbook/namespaces.html

内部模块

“内部模块”现在叫做“命名空间”。 另外,任何使用 module关键字来声明一个内部模块的地方都应该使用namespace关键字来替换。 这就避免了让新的使用者被相似的名称所迷惑。

  • namespace / module 一般称之为内部模块。现在有了更方便的import / export,这个就不怎么用了。
  • 外部模块 import / export。
  • 在代码量较大的情况下,为了避免命名空间冲突,可以将相似的函数、类、接口放置到命名空间内
  • 命名空间可以将代码包裹起来,只对外暴露需要在外部访问的对象,命名空间内通过 export 向外导出
  • 命名空间是内部模块,主要用于组织代码,避免命名冲突
  • 两个重名的命名空间会合并, 但是如果里面有重名的属性会报错。
  • 命名空间可以嵌套命名空间。

原理

其实一个命名空间本质上是一个对象,它的作用是将一系列相关的全局变量组织到一个对象的属性。

  1. namespace Numbers {
  2. export let a = 1;
  3. export let b = 2;
  4. }
  5. // 编译为
  6. var Numbers;
  7. (function (Numbers) {
  8. Numbers.a = 1;
  9. Numbers.b = 2;
  10. })(Numbers || (Numbers = {}));

内部划分

  • 不加export,namespace是在全局模块中划分空间。
  • 加上export之后,namespace就是划分的本模块的局部空间。

1.ts

  1. export namespace x {
  2. export class Dog{
  3. eat(){ console.log('x-Dog-eat'); }
  4. }
  5. }
  6. export namespace y {
  7. export class Dog{
  8. eat(){ console.log('y-Dog-eat'); }
  9. }
  10. }
  11. let x_dog = new x.Dog();
  12. x_dog.eat(); //x-Dog-eat
  13. let y_dog = new y.Dog();
  14. y_dog.eat(); //x-Dog-eat

2.ts

  1. import { x, y } from './2'
  2. let x_dog = new x.Dog();
  3. x_dog.eat(); //x-Dog-eat
  4. let y_dog = new y.Dog();
  5. y_dog.eat(); //x-Dog-eat

分离多文件

  • 当应用变得越来越大时,可以将代码分离到不同的文件中以便于维护。
  • 相同名字的namespace,由于处于一个命名空间,可以合并,并且在使用的时候就如同它们在一个文件中定义的一样。

别名

另一种简化命名空间操作的方法是使用import q = x.y.z给常用的对象起一个短的名字。 不要与用来加载模块的import x = require('name')语法弄混了,这里的语法是为指定的符号创建一个别名。 你可以用这种方法为任意标识符创建别名,也包括导入的模块中的对象。

  1. namespace Shapes {
  2. export namespace Polygons {
  3. export class Triangle { }
  4. export class Square { }
  5. }
  6. }
  7. import polygons = Shapes.Polygons;
  8. let sq = new polygons.Square(); // Same as "new Shapes.Polygons.Square()"

注意,我们并没有使用require关键字,而是直接使用导入符号的限定名赋值。 这与使用 var相似,但它还适用于类型和导入的具有命名空间含义的符号。 重要的是,对于值来讲, import会生成与原始符号不同的引用,所以改变别名的var值并不会影响原始变量的值。

文件、模块、命名空间

文件和模块

每个 module都不一样。
image.png
src\table1.ts

  1. export module Box{
  2. export class Book1{}
  3. }

src\table2.ts

  1. export module Box{
  2. export class Book1{}
  3. }

src\table3.ts

  1. export module Box{
  2. export class Book1{}
  3. }
  1. module A{
  2. export namespace Box{
  3. export const a = 1;
  4. }
  5. }
  6. //如果想获取模块A里面的东西,A里面的东西,必须要加导出
  7. module B{
  8. export namespace Box{
  9. export const a = 1;
  10. console.log(A.Box.a); //1
  11. }
  12. }

空间

namespace 和 module 不一样,namespace 在全局空间中具有唯一性.

在不同的外部模块不会合并
  1. module A{
  2. namespace Box{
  3. export class Book1{}
  4. }
  5. }
  6. module B{
  7. namespace Box{
  8. export class Book2{}
  9. }
  10. console.log(Box); //{ Book2: [Function: Book2] }
  11. }

在全局模块里会合并
  1. //1.ts
  2. namespace Box{
  3. export class Book{}
  4. }
  5. //2.ts
  6. namespace Box{
  7. export class Book{} //error,“Book” 重复
  8. }

文件

每个文件是独立的。
src\table1.ts

  1. export class Book1 { }

src\table2.ts

  1. export class Book1 { }

src\table3.ts

  1. 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语法是没有类型提示的

  1. es6 模块和commonjs模块不兼容,有可能别人的包是commonjs打包出的结果,es6引入就会出问题,所以 ts 为了支持commonjs语法,单独的提出了一个导出方式。`export = str` / `import two = require()`
  2. ```typescript
  3. //node下用commonjs写ts的话
  4. //2.ts
  5. const str:string = 'jack';
  6. export = str;
  7. //1.ts
  8. // let two = require('./2'); //这种不行,无类型提示
  9. // 用ts的时候,除非你引入的模块,它不是ts写的,我们可以使用require直接用
  10. // 如果模块用ts写的,那就需要用 import x = requie(''),这样就会有提示
  11. import two = require('./2');
  12. console.log(two);

如果要是es6模块,全部用 export、export default、export {} / import 即可

  1. //2.ts
  2. const str:string = 'jack';
  3. export default str;
  4. //1.ts
  5. import two from './2';
  6. console.log(two);

使用其它的JavaScript库

为了描述不是用TypeScript编写的类库的类型,我们需要声明类库导出的API。 由于大部分程序库只提供少数的顶级对象,命名空间是用来表示它们的一个好办法。

我们称其为声明是因为它不是外部程序的具体实现。 我们通常在 .d.ts里写这些声明。 如果你熟悉C/C++,你可以把它们当做 .h文件。 让我们看一些例子。

外部命名空间

流行的程序库D3在全局对象d3里定义它的功能。 因为这个库通过一个 <script>标签加载(不是通过模块加载器),它的声明文件使用内部模块来定义它的类型。 为了让TypeScript编译器识别它的类型,我们使用外部命名空间声明。 比如,我们可以像下面这样写:

  1. declare namespace D3 {
  2. export interface Selectors {
  3. select: {
  4. (selector: string): Selection;
  5. (element: EventTarget): Selection;
  6. };
  7. }
  8. export interface Event {
  9. x: number;
  10. y: number;
  11. }
  12. export interface Base extends Selectors {
  13. event: Event;
  14. }
  15. }
  16. declare var d3: D3.Base;