Typescript 实战 --- (10)命名空间和模块

一、命名空间函数

ts 中的 “命名空间” 就是以前的 “内部模块”,任何使用 module 关键字来声明一个内部模块的地方都应该使用 namespace 关键字来替换spa

// ts 中的“内部模块” (废弃)
module X {   }

// ts 中的“命名空间” (推荐)
namespace X {   }

 

(1)、export 关键字3d

使用 export 关键字修饰须要在命名空间以外访问的成员如:接口和类code

// a.ts


namespace Shape {

  let pi = Math.PI;

  export function circle(r: number): number {
    return pi *r * r;
  }

}

Shape.circle(2);

// 变量 pi 是实现的细节,不须要导出,所以在命名空间以外是不能访问的
Shape.pi;    // Error: 类型“typeof Shape”上不存在属性“pi”

 

(2)、分离到多文件对象

多个文件能够共享同一个命名空间以便于维护,尽管是不一样的文件,但还是同一个命名空间。在使用的时候就如同这些文件是在一个文件中定义的同样blog

// b.ts


namespace Shape {
  export function square(width: number) {
    return width ** 2;
  }
}

 

(3)、三斜线指令接口

/// <reference path="..." />

用于声明文件之间的依赖关系,仅可放在包含它的文件最顶端,三斜线指令的前面只能出现单行或多行注释;若是出如今一个语句或声明以后,就会被看成普通的单行注释,而且不具备特殊的含义。ci

上例中,a.ts 和 b.ts 共享同一个命名空间 Shape,在 b.ts 中能够访问到 a.ts 的 circle方法,可是执行的时候会报错字符串

这个时候就须要使用三斜线指令在 b.ts  中引用 a.ts编译器

// b.ts


/// <reference path="./a.ts" />

namespace Shape {
  export function square(width: number) {
    return width ** 2;
  }
}

console.log(Shape.circle(2)); 

编译结果以下:

 

(4)、别名

可使用 import q = x.y.z 给经常使用的对象起一个短的名字

namespace Shape {

  let pi = Math.PI;

  export function circle(r: number): number {
    return pi *r * r;
  }

}

import circle = Shape.circle;
console.log(circle(1));    // 3.141592653589793

 

二、声明合并

所谓 “声明合并” 就是编译器会把程序中多个地方具备相同名称的声明合并为一个声明,好处是能够把程序中散落各处的重名声明合并到一块儿

 

2-一、合并接口

// a.ts 

interface A {
  name: string;
}

interface A {
  age: number;
}

let a: A = {
  name: 'Kobe',
  age: 41
}

console.log(a);   // { name: 'Kobe', age: 41 }

若是是全局模块,接口A的声明合并甚至能够在不一样的文件中,例如新建一个 b.ts 也声明一个接口A,原来a.ts中的变量a就不能正确输出了

// b.ts

interface A {
  gender: string;
}

 

2-1-一、接口的非函数成员

接口中的非函数成员必须保持惟一性,若是不惟一的话,它们的类型必须相同

// a.ts 

interface A {
  name: string;
  age: number;
}

interface A {
  name: number;
  age: number;
}

// 两个接口中,成员age的类型相同,成员name的类型不相同,编译器提示报错

 

2-1-二、接口的函数成员

接口中的每个函数成员都会被声明成 函数重载

// a.ts 

interface A {
  name: string;
  foo(x: number): number;
}

interface A {
  age: number;
  foo(x: string): string;
  foo(x: number[]): number[];
}

// 实现接口A时,函数foo须要返回一个 any类型
let a: A = {
  name: 'Kobe',
  age: 41,
  foo(x: any) {
    return x
  }
}

函数重载的时候,须要注意函数声明的顺序,由于编译器会按顺序进行匹配。那么,在接口合并时,这些顺序是如何肯定的呢?

(1)、在接口的内部,按照书写的顺序来肯定;

// a.ts 

interface A {
  age: number;
  foo(x: string): string;  // 1
  foo(x: number[]): number[];  // 2
}

 

(2)、接口之间,后面的接口排在前面

// a.ts 

interface A {
  name: string;
  foo(x: number): number;  // 3
}

interface A {
  age: number;
  foo(x: string): string;  // 1
  foo(x: number[]): number[];  // 2
}

 

(3)、若是一个函数的参数是一个字符串字面量,这个声明将会提高到整个函数声明的最顶端

// a.ts 

interface A {
  name: string;
  foo(x: number): number;  // 5
  foo(x: 'a'): string;  // 2
}

interface A {
  age: number;
  foo(x: string): string;  // 3
  foo(x: number[]): number[];  // 4
  foo(x: 'b'): string;  // 1
}

 

2-二、合并命名空间

(1)、命名空间中导出的成员是不能够重复定义的,这一点与合并接口不一样

// a.ts 

namespace Shape {
  const pi = Math.PI;

  export function square(r: number) {
    return pi * r ** 2;
  }
}


// b.ts

namespace Shape {
  export function square(width: number) {
    return width * width;
  }
}

 

(2)、合并命名空间和函数  --- 至关于给函数添加了属性

// a.ts 

function Lib() {}

namespace Lib {
  export let str = 'hello world';
}

console.log(Lib.str);  // hello world

 

(3)、合并命名空间和类  --- 至关于给类添加了静态属性

// a.ts 

class C {}

namespace C {
  export let str = 'hello world';
}

console.log(C.str);  // hello world

 

注意:命名空间与函数或者类合并时,必需要放在函数或者类的后面,不然会报错

 

2-三、合并枚举

合并命名空间与枚举,相对于给枚举添加了属性。同时须要注意,命名空间与枚举的位置不受限制

// a.ts 

// 注意命名空间与枚举的位置
namespace Color {
  export function mix() {}
}

enum Color {
  Red,
  Green,
  Blue
}

console.log(Color);  

相关文章
相关标签/搜索