Appearance
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 支持两种模块解析策略:
- 经典解析(Classic):主要用于 AMD/SystemJS 模块
- 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 应用至关重要。