参考文章:

  1. TypeScript中文手册——命名空间

什么是命名空间

旧版本 TypeScript 中,命名空间叫作”内部模块”,新版本更名为”命名空间”,写法由module X {}改为namespace X {}

命名空间是组织代码的一种手段,可以将类型、class 等封装在命名空间中,以防止环境污染,如果要让命名空间外部能访问命名空间内的成员,可以用 export 将这些成员导出。

举个例子:

  1. namespace IM {
  2. class Account {
  3. id: string;
  4. name: string;
  5. constructor(id: string, name: string) {
  6. this.id = id;
  7. this.name = name;
  8. }
  9. }
  10. class Session {
  11. id: string;
  12. type: 'P2P' | 'Team';
  13. constructor(id: string, type: 'P2P' | 'Team') {
  14. this.id = id;
  15. this.type = type;
  16. }
  17. }
  18. export const createAccount = (id: string, name: string) => {
  19. return new Account(id, name);
  20. }
  21. }
  22. const accountA = IM.createAccount('1', 'A');
  23. // const accountB = new IM.Account('2', 'B'); // 由于Account没有导出,所以此处会报错

以上代码我们命名了一个名为 IM 的命名空间,其中有三个成员:

  1. 用户类 Account;
  2. 会话类 Session;
  3. 创建用户实例的函数createAccount()

其中createAccount()用 export 导出,使得空间外部可以访问,接着,我们可以借助const accountA = IM.createAccount('1', 'A')创建一个 IM Account 实例,由于 Account 没有导出,故不能通过new IM.Account('2', 'B')创建。

为什么要把 Account、Session 封装到命名空间内且不导出呢?这是为了防止全局污染,因为 Account 和 Session 是比较泛用的名词,如果不封闭处理,随时可能在上下文中冒出其他 Account 或者 Session,从而导致程序异常。

多文件中的命名空间

当应用变得越来越大时,我们需要把代码分割到多个文件中以方便维护。命名空间也支持多文件,每个文件都是一个命名空间,在主文件中通过三斜线指令(如/// <reference path="Validation.ts" />)引入命名空间即可。

举个例子,我们先将上节写的命名空间 IM 的代码写入到 IM.ts 文件中:

  1. namespace IM {
  2. export interface AccountType {
  3. id: string;
  4. name: string;
  5. }
  6. export class Account {
  7. data: AccountType;
  8. // 只需传入用户名称即可自动生成一个Account数据
  9. constructor(name: string) {
  10. this.data = {
  11. id: new Date().valueOf().toString(),
  12. name,
  13. };
  14. }
  15. }
  16. }

然后同级目录下创建一个业务命名空间 Business,文件名 Business.ts:

  1. /// <reference path="./IM.ts" />
  2. namespace Business {
  3. export interface AccountType {
  4. id: string;
  5. nickName: string;
  6. phoneNum: string;
  7. imAccount?: IM.AccountType;
  8. }
  9. export class Account {
  10. data: AccountType;
  11. constructor(_data: AccountType) {
  12. this.data = _data;
  13. }
  14. }
  15. }

以上代码通过三斜线指令引入 IM.ts,引用到的只是其中的 IM.AccountType 这个接口,可以看到业务 Account 数据中的 imAccount 即 IM 用户数据。

接着在同级目录下的主文件中写如以下代码:

  1. /// <reference path="./IM.ts" />
  2. /// <reference path="./Business.ts" />
  3. const createAccount = (baseInfo: Business.AccountType) => {
  4. const imAccount = new IM.Account(baseInfo.nickName);
  5. const businessAccount = new Business.Account({
  6. ...baseInfo,
  7. imAccount: imAccount.data,
  8. });
  9. return businessAccount;
  10. };
  11. const account = createAccount({
  12. id: '1',
  13. nickName: 'WJT',
  14. phoneNum: '1008611',
  15. });
  16. console.log(account.data);

主文件同时引入 IM 和 Business 两个命名空间,然后编写了一个createAccount()函数用于根据基础数据创建业务 Account 和 IM Account,并将两者组合在一起。

使用tsc --outFile test.js test.ts编译(可以把编译结果全部输出到一个文件中)生成 test.js 文件,运行打印结果:

  1. {
  2. id: '1',
  3. nickName: 'WJT',
  4. phoneNum: '1008611',
  5. imAccount: { id: '1649756946688', name: 'WJT' }
  6. }