Skip to content
On this page

TypeScript 模块与命名空间

TypeScript 提供了模块和命名空间两种机制来组织代码,使大型应用程序更易于管理。

模块(Modules)

TypeScript 中的模块遵循 ES6 模块语法,也支持 CommonJS 和 AMD 模块系统。

导出(Export)

typescript
// stringValidator.ts
export interface StringValidator {
  isAcceptable(s: string): boolean;
}

export const numberRegexp = /^[0-9]+$/;

export class ZipCodeValidator implements StringValidator {
  isAcceptable(s: string) {
    return s.length === 5 && numberRegexp.test(s);
  }
}

// 导出函数
export function validate(input: string): boolean {
  return input.length > 0;
}

// 导出类型
export type ValidationResult = {
  isValid: boolean;
  message?: string;
};

// 导出命名空间
export namespace Validation {
  export class EmailValidator implements StringValidator {
    isAcceptable(s: string) {
      return s.includes('@');
    }
  }
}

重命名导出

typescript
// reExports.ts
import { ZipCodeValidator as ZCV } from "./stringValidator";
export { ZCV as RegExpBasedZipCodeValidator };

默认导出

typescript
// 使用默认导出
export default class Greeting {
  constructor(public message: string) {}
  
  greet() {
    return this.message;
  }
}

// 默认导出函数
export default function (name: string) {
  return `Hello, ${name}!`;
}

// 默认导出值
const defaultName = "John";
export default defaultName;

导入(Import)

typescript
// 引入整个模块
import * as validator from "./stringValidator";

// 引入特定导出
import { ZipCodeValidator } from "./stringValidator";

// 重命名导入
import { ZipCodeValidator as MyValidator } from "./stringValidator";

// 引入默认导出
import defaultExport from "./stringValidator";

// 引入默认和命名导出
import defaultExport2, { ZipCodeValidator } from "./stringValidator";

// 引入所有并重命名
import defaultExport3, * as validator2 from "./stringValidator";

// 仅运行模块,不导入任何内容
import "./my-module";

导入类型

typescript
// 仅导入类型定义
import type { StringValidator } from "./stringValidator";

// 混合导入
import { ZipCodeValidator, type StringValidator } from "./stringValidator";

命名空间(Namespaces)

命名空间是一种在全局作用域内组织代码的方式,避免全局污染。

基本命名空间

typescript
namespace Validation {
  export interface StringValidator {
    isAcceptable(s: string): boolean;
  }

  const lettersRegexp = /^[A-Za-z]+$/;
  const numberRegexp = /^[0-9]+$/;

  export class LettersOnlyValidator implements StringValidator {
    isAcceptable(s: string) {
      return lettersRegexp.test(s);
    }
  }

  export class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string) {
      return s.length === 5 && numberRegexp.test(s);
    }
  }
}

// 使用命名空间
let strings = ["Hello", "98052", "101"];

let validators: { [s: string]: Validation.StringValidator; } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();

for (let s of strings) {
  for (let name in validators) {
    let isMatch = validators[name].isAcceptable(s);
    console.log(`'${s}' ${isMatch ? "matches" : "does not match"} '${name}'.`);
  }
}

嵌套命名空间

typescript
namespace Shapes {
  export namespace Polygons {
    export class Triangle { }
    export class Square { }
  }
}

let triangle = new Shapes.Polygons.Triangle();

多文件命名空间

// Validation.ts

typescript
namespace Validation {
  export interface StringValidator {
    isAcceptable(s: string): boolean;
  }
}

// LettersOnlyValidator.ts

typescript
/// <reference path="Validation.ts" />
namespace Validation {
  const lettersRegexp = /^[A-Za-z]+$/;
  export class LettersOnlyValidator implements StringValidator {
    isAcceptable(s: string) {
      return lettersRegexp.test(s);
    }
  }
}

// ZipCodeValidator.ts

typescript
/// <reference path="Validation.ts" />
namespace Validation {
  const numberRegexp = /^[0-9]+$/;
  export class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string) {
      return s.length === 5 && numberRegexp.test(s);
    }
  }
}

模块与命名空间的比较

模块的优势

typescript
// 模块更现代,支持 tree-shaking
import { ZipCodeValidator } from "./Validation";

// 模块支持复杂的依赖关系
import * as fs from "fs";
import path = require("path");

// 模块可以使用 import() 进行动态导入
async function loadModule() {
  const validator = await import('./Validation');
  return new validator.ZipCodeValidator();
}

命名空间的优势

typescript
// 命名空间适合简单的组织方式
namespace MyLibrary {
  export function utilityFunction() { }
  export class HelperClass { }
}

// 命名空间在浏览器环境中更容易使用
// <script src="MyLibrary.js"></script>
// MyLibrary.utilityFunction();

模块解析策略

相对与非相对模块导入

typescript
// 相对导入(以 /、./ 或 ../ 开头)
import { ZipCodeValidator } from "./Validation";
import { test } from "../test/TestHelper";
import * as utils from "/utils/UtilityFunctions";

// 非相对导入
import { Component } from "@angular/core";
import * as _ from "lodash";

模块解析算法

TypeScript 支持两种模块解析策略:

  1. 经典解析(Classic):主要用于 AMD/SystemJS 模块
  2. Node 解析:模拟 Node.js 模块解析机制

路径映射

tsconfig.json 中配置路径映射:

json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "utils/*": ["src/utils/*"],
      "components/*": ["src/components/*"],
      "test/*": ["__tests__/*"]
    }
  }
}

使用路径映射:

typescript
// 使用映射路径
import { UserService } from "@/services/UserService";
import { formatDate } from "utils/DateUtils";
import { Button } from "components/Button";

高级模块概念

动态导入

typescript
// 动态导入表达式
async function loadLodash() {
  const _ = await import('lodash');
  return _.default;
}

// 条件导入
async function loadModule(condition: boolean) {
  if (condition) {
    const module1 = await import('./moduleA');
    return module1;
  } else {
    const module2 = await import('./moduleB');
    return module2;
  }
}

// 在函数中使用动态导入
function getComponentAsync() {
  return import('./MyComponent').then(component => {
    return component.default;
  });
}

模块声明

typescript
// 为第三方 JavaScript 库声明类型
declare module "hot-new-module" {
  export class NewModule {
    constructor(config?: NewModuleConfig);
    doStuff(): void;
  }
  export interface NewModuleConfig {
    version: number;
  }
}

// 为现有模块添加声明
declare module "./MyModule" {
  interface AdditionalInterface {
    additionalProperty: string;
  }
}

全局扩充

typescript
// 扩充全局变量
declare global {
  interface Window {
    myCustomProperty: string;
  }
  
  var process: {
    env: {
      NODE_ENV: 'development' | 'production';
    };
  };
}

// 在模块中扩充全局作用域
export {}; // 确保这是一个模块

模块设计模式

单例模式

typescript
// singleton.ts
export class Database {
  private static instance: Database;
  
  private constructor() { }
  
  static getInstance(): Database {
    if (!Database.instance) {
      Database.instance = new Database();
    }
    return Database.instance;
  }
  
  connect() {
    console.log("Connected to database");
  }
}

// 使用
import { Database } from './singleton';
const db = Database.getInstance();

工厂模式

typescript
// factory.ts
interface Animal {
  makeSound(): string;
}

class Dog implements Animal {
  makeSound() { return "Woof!"; }
}

class Cat implements Animal {
  makeSound() { return "Meow!"; }
}

type AnimalType = "dog" | "cat";

export class AnimalFactory {
  static create(type: AnimalType): Animal {
    switch (type) {
      case "dog": return new Dog();
      case "cat": return new Cat();
      default: throw new Error("Unknown animal type");
    }
  }
}

// 使用
import { AnimalFactory } from './factory';
const dog = AnimalFactory.create("dog");
console.log(dog.makeSound());

编译与打包配置

tsconfig.json 配置

json
{
  "compilerOptions": {
    "module": "commonjs",        // 模块系统
    "target": "es2017",          // 编译目标
    "moduleResolution": "node",  // 模块解析策略
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}

小结

模块和命名空间都是组织 TypeScript 代码的重要方式。模块是现代 JavaScript 和 TypeScript 推荐的代码组织方式,支持 tree-shaking 和更好的依赖管理。命名空间在某些场景下仍然有用,特别是在浏览器环境中或需要向后兼容的项目中。理解这两种组织方式的差异和使用场景对于构建大型 TypeScript 应用至关重要。