TypeScript 命名空间

TypeScript 中的命名空间(Namespaces)详解

命名空间(Namespaces)(以前称为“内部模块”)是 TypeScript 早期用于组织代码、避免全局命名冲突的一种机制。它通过 namespace 关键字将相关类型、函数、类等逻辑分组到一个命名空间内。

注意:在现代 TypeScript 项目中(ES Modules 时代),命名空间已逐渐被 ES6 模块(import/export)取代,官方推荐优先使用模块。但在某些场景(如旧项目、声明文件 .d.ts、或需要声明合并时),命名空间仍有用途。

1. 基本语法与使用

// 定义命名空间
namespace Utils {
  export function log(message: string): void {
    console.log(`[LOG] ${message}`);
  }

  export const VERSION = "1.0.0";

  export class Calculator {
    add(a: number, b: number): number {
      return a + b;
    }
  }

  // 不加 export 的成员仅在命名空间内部可见
  function internalHelper() {
    console.log("内部工具");
  }
}

// 使用命名空间
Utils.log("启动应用");              // OK
console.log(Utils.VERSION);         // "1.0.0"

let calc = new Utils.Calculator();
console.log(calc.add(5, 3));        // 8

// internalHelper();                // 错误:外部不可访问

关键点

  • 必须使用 export 才能从命名空间外部访问成员。
  • 命名空间可以嵌套。

2. 嵌套命名空间

namespace App {
  export namespace Math {
    export function sum(numbers: number[]): number {
      return numbers.reduce((a, b) => a + b, 0);
    }
  }

  export namespace UI {
    export class Button {
      label: string;
      constructor(label: string) {
        this.label = label;
      }
    }
  }
}

// 使用
App.Math.sum([1, 2, 3]);            // 6
new App.UI.Button("提交");

3. 命名空间别名(Alias)

使用 import 为命名空间起别名,简化长路径访问:

import Calc = App.Math;

Calc.sum([10, 20]);  // 30

4. 命名空间与模块的混合使用

命名空间可以与 ES 模块共存(但不推荐混用):

// file: utils.ts
export namespace StringUtils {
  export function capitalize(str: string): string {
    return str.charAt(0).toUpperCase() + str.slice(1);
  }
}

// file: main.ts
import { StringUtils } from "./utils";

StringUtils.capitalize("hello");  // "Hello"

5. 命名空间的声明合并(Declaration Merging)

同名命名空间会自动合并(类似接口合并):

namespace Validation {
  export function isEmail(str: string): boolean {
    return /\S+@\S+\.\S+/.test(str);
  }
}

namespace Validation {
  export function isPhone(str: string): boolean {
    return /^\d{10,11}$/.test(str);
  }
}

// 合并后 Validation 有两个函数
Validation.isEmail("test@example.com");  // true
Validation.isPhone("13800138000");      // true

这在扩展第三方库时非常有用。

6. 命名空间 vs ES 模块(import/export)对比

特性命名空间 (namespace)ES 模块 (import/export)
推荐程度旧项目、声明文件现代项目首选
模块系统基于全局或脚本加载原生 ES Modules,支持 tree-shaking
导出方式export 在 namespace 内export 在文件顶层
导入方式点访问(如 Utils.log)或别名import { ... } from "./file"
声明合并支持(同名自动合并)不支持
编译输出通常生成全局 IIFE 或 UMD生成 ES/CommonJS 模块
适用场景旧代码、.d.ts 文件、内部组织逻辑所有现代项目(React、Vue、Node.js)

官方建议

  • 新项目:完全使用 ES 模块import/export)。
  • 仅在以下情况使用命名空间:
  • 维护旧项目(TypeScript < 1.5 时代代码)。
  • 编写声明文件(.d.ts)扩展全局库(如 jQuery)。
  • 需要声明合并的特殊场景。

7. 在声明文件中的典型使用(.d.ts)

// jquery.d.ts 示例(简化版)
declare namespace JQuery {
  interface JQueryInstance {
    html(content: string): this;
    on(event: string, handler: Function): this;
  }

  function $(selector: string): JQueryInstance;
}

declare function $(readyFunc: () => void): void;

// 使用(全局模式)
$("#app").html("Hello").on("click", () => alert("clicked"));

8. 编译选项(tsconfig.json)

使用命名空间时,通常需要配置:

{
  "compilerOptions": {
    "module": "commonjs",      // 或 amd、umd(不推荐 ESNext)
    "target": "es5",
    "outFile": "./dist/app.js" // 将所有命名空间编译到一个文件(可选)
  }
}

现代项目推荐:

{
  "module": "ESNext",
  "moduleResolution": "node"
}

9. 最佳实践建议

  1. 新项目避免使用 namespace,改用 ES 模块 + 文件划分。
  2. 组织代码时优先用文件夹 + import/export
  3. 如果必须使用命名空间
  • 保持命名空间小而专注。
  • 所有外部成员加 export
  • 使用别名简化访问。
  1. 迁移旧项目:逐步将 namespace 转为模块。

小结:何时使用命名空间?

场景推荐方式
新项目代码组织ES 模块(import/export)
旧项目维护命名空间(逐步迁移)
扩展全局第三方库(.d.ts)命名空间 + declare
需要声明合并命名空间或 interface

结论:在 2025 年的现代 TypeScript 开发中,命名空间已基本被淘汰,除特殊场景外,应始终优先使用 ES6 模块系统

如果您想看如何将命名空间迁移到模块声明文件的实际示例,或者其他高级主题(如模块增强、全局类型声明),请告诉我!

文章已创建 3383

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部