Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

TypeScript模块

TypeScript作为JavaScript的超集,为模块化开发带来了强大的类型系统支持。它不仅提供了编译时类型检查,还增强了模块的导入导出机制,为大型项目的模块化架构提供了坚实的基础。

TypeScript模块系统

模块语法增强

TypeScript在ES模块基础上增加了类型信息:

// 类型导出
export type UserType = {
  id: number;
  name: string;
  email: string;
};

export interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

// 值和类型同时导出
export class UserService {
  async getUser(id: number): Promise<UserType> {
    // 实现...
  }
}

// 命名空间导出
export namespace Utils {
  export function formatDate(date: Date): string {
    return date.toISOString();
  }
  
  export type DateFormat = 'ISO' | 'Local' | 'UTC';
}

// 条件类型导出
export type ApiEndpoint<T extends string> = T extends 'users' 
  ? UserType[] 
  : T extends 'posts' 
  ? PostType[] 
  : unknown;

模块导入的类型支持

// 类型导入
import type { UserType, ApiResponse } from './types';
import type * as Types from './types';

// 值导入
import { UserService } from './services';
import { Utils } from './utils';

// 混合导入
import { CONFIG, type ConfigType } from './config';

// 动态导入与类型
const loadModule = async (): Promise<typeof import('./heavy-module')> => {
  return await import('./heavy-module');
};

// 条件导入
type ModuleType = typeof import('./module');
type AsyncModuleType = Awaited<typeof import('./async-module')>;

模块声明和环境声明

// 全局模块声明
declare global {
  interface Window {
    __APP_CONFIG__: AppConfig;
  }
  
  namespace NodeJS {
    interface ProcessEnv {
      NODE_ENV: 'development' | 'production' | 'test';
      API_URL: string;
    }
  }
}

// 模块声明
declare module '*.vue' {
  import { DefineComponent } from 'vue';
  const component: DefineComponent<{}, {}, any>;
  export default component;
}

declare module '*.module.css' {
  const classes: { readonly [key: string]: string };
  export default classes;
}

// 扩展已有模块
declare module 'express' {
  interface Request {
    user?: User;
  }
}

// 第三方库类型声明
declare module 'some-untyped-library' {
  export function someFunction(arg: string): number;
  export const CONSTANT: string;
}

编译配置

tsconfig.json详解

{
  "compilerOptions": {
    // 模块系统配置
    "module": "ES2022",
    "moduleResolution": "node",
    "target": "ES2020",
    
    // 模块检测
    "moduleDetection": "auto",
    
    // 输出控制
    "outDir": "./dist",
    "rootDir": "./src",
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    
    // 模块解析
    "baseUrl": "./",
    "paths": {
      "@/*": ["src/*"],
      "@components/*": ["src/components/*"],
      "@utils/*": ["src/utils/*"]
    },
    
    // 类型检查
    "strict": true,
    "noImplicitReturns": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    
    // ES模块互操作
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true,
    
    // 实验性特性
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    
    // 增量编译
    "incremental": true,
    "tsBuildInfoFile": ".tsbuildinfo",
    
    // 类型导入
    "verbatimModuleSyntax": false,
    "allowImportingTsExtensions": false
  },
  
  // 项目引用
  "references": [
    { "path": "./packages/core" },
    { "path": "./packages/utils" }
  ],
  
  // 包含和排除
  "include": [
    "src/**/*",
    "types/**/*"
  ],
  "exclude": [
    "node_modules",
    "dist",
    "**/*.test.ts"
  ]
}

多包项目配置

// packages/core/tsconfig.json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "./src",
    "composite": true
  },
  "include": ["src/**/*"],
  "references": []
}

// packages/utils/tsconfig.json  
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "./src",
    "composite": true
  },
  "include": ["src/**/*"],
  "references": [
    { "path": "../core" }
  ]
}

// tsconfig.base.json
{
  "compilerOptions": {
    "module": "ES2022",
    "target": "ES2020",
    "moduleResolution": "node",
    "strict": true,
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "incremental": true
  }
}

高级模块模式

模块扩展模式

// 基础模块
// base-module.ts
export interface BaseConfig {
  name: string;
  version: string;
}

export class BaseService {
  constructor(protected config: BaseConfig) {}
  
  getName(): string {
    return this.config.name;
  }
}

// 扩展模块
// extended-module.ts
import { BaseConfig, BaseService } from './base-module';

export interface ExtendedConfig extends BaseConfig {
  features: string[];
  debug: boolean;
}

export class ExtendedService extends BaseService {
  constructor(protected config: ExtendedConfig) {
    super(config);
  }
  
  getFeatures(): string[] {
    return this.config.features;
  }
  
  isDebugEnabled(): boolean {
    return this.config.debug;
  }
}

// 模块聚合
export * from './base-module';
export { ExtendedService, type ExtendedConfig } from './extended-module';

插件架构模式

// 插件系统类型定义
export interface Plugin<T = any> {
  name: string;
  version: string;
  install(app: App, options?: T): void;
  uninstall?(app: App): void;
}

export interface App {
  use<T>(plugin: Plugin<T>, options?: T): this;
  unuse(pluginName: string): this;
  getPlugin<T extends Plugin>(name: string): T | undefined;
}

// 插件实现
export class ValidationPlugin implements Plugin<ValidationOptions> {
  name = 'validation';
  version = '1.0.0';
  
  install(app: App, options: ValidationOptions = {}) {
    // 安装验证插件
    app.addValidator(new Validator(options));
  }
  
  uninstall(app: App) {
    app.removeValidator(this.name);
  }
}

// 应用实现
export class Application implements App {
  private plugins = new Map<string, Plugin>();
  private validators = new Map<string, Validator>();
  
  use<T>(plugin: Plugin<T>, options?: T): this {
    if (this.plugins.has(plugin.name)) {
      throw new Error(`Plugin ${plugin.name} already installed`);
    }
    
    plugin.install(this, options);
    this.plugins.set(plugin.name, plugin);
    return this;
  }
  
  unuse(pluginName: string): this {
    const plugin = this.plugins.get(pluginName);
    if (plugin?.uninstall) {
      plugin.uninstall(this);
    }
    this.plugins.delete(pluginName);
    return this;
  }
  
  getPlugin<T extends Plugin>(name: string): T | undefined {
    return this.plugins.get(name) as T;
  }
  
  addValidator(validator: Validator): void {
    this.validators.set(validator.name, validator);
  }
  
  removeValidator(name: string): void {
    this.validators.delete(name);
  }
}

工厂模式模块

// 工厂接口
export interface Factory<T> {
  create(...args: any[]): T;
  canHandle(type: string): boolean;
}

// 具体工厂
export class HttpClientFactory implements Factory<HttpClient> {
  canHandle(type: string): boolean {
    return ['axios', 'fetch', 'xhr'].includes(type);
  }
  
  create(type: 'axios' | 'fetch' | 'xhr', config?: any): HttpClient {
    switch (type) {
      case 'axios':
        return new AxiosClient(config);
      case 'fetch':
        return new FetchClient(config);
      case 'xhr':
        return new XhrClient(config);
      default:
        throw new Error(`Unsupported client type: ${type}`);
    }
  }
}

// 工厂注册器
export class FactoryRegistry {
  private factories = new Map<string, Factory<any>>();
  
  register<T>(name: string, factory: Factory<T>): void {
    this.factories.set(name, factory);
  }
  
  create<T>(name: string, type: string, ...args: any[]): T {
    const factory = this.factories.get(name);
    if (!factory) {
      throw new Error(`Factory ${name} not found`);
    }
    
    if (!factory.canHandle(type)) {
      throw new Error(`Factory ${name} cannot handle type ${type}`);
    }
    
    return factory.create(type, ...args);
  }
}

// 使用示例
const registry = new FactoryRegistry();
registry.register('http', new HttpClientFactory());

const client = registry.create<HttpClient>('http', 'axios', {
  baseURL: 'https://api.example.com'
});

类型生成和导出

自动类型生成

// type-generator.ts
import * as ts from 'typescript';
import * as fs from 'fs';

export class TypeGenerator {
  private program: ts.Program;
  private checker: ts.TypeChecker;
  
  constructor(configPath: string) {
    const config = ts.readConfigFile(configPath, ts.sys.readFile);
    const parseResult = ts.parseJsonConfigFileContent(
      config.config,
      ts.sys,
      '.'
    );
    
    this.program = ts.createProgram(
      parseResult.fileNames,
      parseResult.options
    );
    this.checker = this.program.getTypeChecker();
  }
  
  generateApiTypes(sourceFile: string): string {
    const source = this.program.getSourceFile(sourceFile);
    if (!source) {
      throw new Error(`Source file ${sourceFile} not found`);
    }
    
    const types: string[] = [];
    
    ts.forEachChild(source, node => {
      if (ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node)) {
        const type = this.checker.getTypeAtLocation(node);
        const typeString = this.checker.typeToString(type);
        types.push(`export type ${node.name.text} = ${typeString};`);
      }
    });
    
    return types.join('\n');
  }
  
  generateSchemaTypes(schema: any): string {
    // 从JSON Schema生成TypeScript类型
    const generateType = (obj: any, name: string): string => {
      if (obj.type === 'object') {
        const properties = Object.entries(obj.properties || {})
          .map(([key, value]: [string, any]) => {
            const optional = !obj.required?.includes(key) ? '?' : '';
            const type = this.mapJsonSchemaType(value);
            return `${key}${optional}: ${type}`;
          })
          .join(';\n  ');
        
        return `export interface ${name} {\n  ${properties}\n}`;
      }
      
      return `export type ${name} = ${this.mapJsonSchemaType(obj)};`;
    };
    
    return generateType(schema, 'GeneratedType');
  }
  
  private mapJsonSchemaType(schema: any): string {
    switch (schema.type) {
      case 'string':
        return schema.enum ? schema.enum.map((v: string) => `'${v}'`).join(' | ') : 'string';
      case 'number':
      case 'integer':
        return 'number';
      case 'boolean':
        return 'boolean';
      case 'array':
        return `${this.mapJsonSchemaType(schema.items)}[]`;
      case 'object':
        return 'object';
      default:
        return 'unknown';
    }
  }
}

声明文件生成

// declaration-bundler.ts
export class DeclarationBundler {
  constructor(private options: {
    input: string;
    output: string;
    external?: string[];
  }) {}
  
  async bundle(): Promise<void> {
    const program = ts.createProgram([this.options.input], {
      declaration: true,
      emitDeclarationOnly: true,
      moduleResolution: ts.ModuleResolutionKind.NodeJs,
      module: ts.ModuleKind.ES2022,
      target: ts.ScriptTarget.ES2020
    });
    
    const declarations = new Map<string, string>();
    
    // 收集所有声明
    program.emit(undefined, (fileName, text) => {
      if (fileName.endsWith('.d.ts')) {
        declarations.set(fileName, text);
      }
    });
    
    // 合并声明
    const bundled = this.mergeDeclarations(declarations);
    
    // 写入输出文件
    fs.writeFileSync(this.options.output, bundled);
  }
  
  private mergeDeclarations(declarations: Map<string, string>): string {
    const imports = new Set<string>();
    const exports = new Set<string>();
    let content = '';
    
    for (const [fileName, text] of declarations) {
      // 解析导入导出
      const lines = text.split('\n');
      
      for (const line of lines) {
        if (line.startsWith('import ')) {
          imports.add(line);
        } else if (line.startsWith('export ')) {
          exports.add(line);
        } else if (line.trim() && !line.startsWith('//')) {
          content += line + '\n';
        }
      }
    }
    
    // 组装最终输出
    const result = [
      ...Array.from(imports),
      '',
      content,
      '',
      ...Array.from(exports)
    ].join('\n');
    
    return result;
  }
}

编译优化

增量编译

// incremental-compiler.ts
export class IncrementalCompiler {
  private program: ts.SemanticDiagnosticsBuilderProgram;
  private host: ts.CompilerHost;
  
  constructor(private configPath: string) {
    this.setupCompiler();
  }
  
  private setupCompiler(): void {
    const config = ts.readConfigFile(this.configPath, ts.sys.readFile);
    const parseResult = ts.parseJsonConfigFileContent(
      config.config,
      ts.sys,
      '.'
    );
    
    this.host = ts.createIncrementalCompilerHost(parseResult.options);
    
    this.program = ts.createIncrementalProgram({
      rootNames: parseResult.fileNames,
      options: {
        ...parseResult.options,
        incremental: true,
        tsBuildInfoFile: '.tsbuildinfo'
      },
      host: this.host
    });
  }
  
  compile(): ts.Diagnostic[] {
    const emitResult = this.program.emit();
    const diagnostics = [
      ...this.program.getConfigFileParsingDiagnostics(),
      ...this.program.getSyntacticDiagnostics(),
      ...this.program.getSemanticDiagnostics(),
      ...emitResult.diagnostics
    ];
    
    return diagnostics;
  }
  
  getAffectedFiles(): string[] {
    const affectedFiles: string[] = [];
    
    while (true) {
      const result = this.program.getSemanticDiagnosticsOfNextAffectedFile();
      if (!result) break;
      
      if (result.affected.fileName) {
        affectedFiles.push(result.affected.fileName);
      }
    }
    
    return affectedFiles;
  }
  
  watchMode(callback: (diagnostics: ts.Diagnostic[]) => void): void {
    const watchProgram = ts.createWatchProgram(
      ts.createWatchCompilerHost(
        this.configPath,
        {},
        ts.sys,
        ts.createSemanticDiagnosticsBuilderProgram,
        (diagnostic) => callback([diagnostic]),
        (diagnostic) => callback([diagnostic])
      )
    );
  }
}

并行类型检查

// parallel-type-checker.ts
import { Worker, isMainThread, parentPort, workerData } from 'worker_threads';

if (!isMainThread) {
  // Worker线程
  const { files, options } = workerData;
  
  const program = ts.createProgram(files, options);
  const diagnostics = ts.getPreEmitDiagnostics(program);
  
  parentPort?.postMessage({
    diagnostics: diagnostics.map(d => ({
      file: d.file?.fileName,
      start: d.start,
      length: d.length,
      messageText: d.messageText,
      category: d.category,
      code: d.code
    }))
  });
} else {
  // 主线程
  export class ParallelTypeChecker {
    async checkFiles(files: string[], options: ts.CompilerOptions): Promise<ts.Diagnostic[]> {
      const chunkSize = Math.ceil(files.length / 4);
      const chunks = [];
      
      for (let i = 0; i < files.length; i += chunkSize) {
        chunks.push(files.slice(i, i + chunkSize));
      }
      
      const workers = chunks.map(chunk => 
        new Worker(__filename, {
          workerData: { files: chunk, options }
        })
      );
      
      const results = await Promise.all(
        workers.map(worker => 
          new Promise<{ diagnostics: any[] }>((resolve, reject) => {
            worker.on('message', resolve);
            worker.on('error', reject);
          })
        )
      );
      
      // 清理workers
      workers.forEach(worker => worker.terminate());
      
      // 合并结果
      return results.flatMap(result => result.diagnostics);
    }
  }
}

构建工具集成

Webpack集成

// webpack.config.ts
import type { Configuration } from 'webpack';
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';

const config: Configuration = {
  entry: './src/index.ts',
  
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: [
          {
            loader: 'ts-loader',
            options: {
              // 只进行转译,类型检查交给ForkTsCheckerWebpackPlugin
              transpileOnly: true,
              
              // 编译选项覆盖
              compilerOptions: {
                module: 'esnext',
                target: 'es2020'
              }
            }
          }
        ],
        exclude: /node_modules/
      }
    ]
  },
  
  plugins: [
    new ForkTsCheckerWebpackPlugin({
      typescript: {
        configFile: 'tsconfig.json',
        diagnosticOptions: {
          semantic: true,
          syntactic: true
        }
      },
      
      // ESLint集成
      eslint: {
        files: './src/**/*.{ts,tsx,js,jsx}'
      }
    })
  ],
  
  resolve: {
    extensions: ['.tsx', '.ts', '.js'],
    alias: {
      '@': path.resolve(__dirname, 'src')
    }
  }
};

export default config;

Vite集成

// vite.config.ts
import { defineConfig } from 'vite';
import typescript from '@rollup/plugin-typescript';

export default defineConfig({
  plugins: [
    typescript({
      tsconfig: './tsconfig.json',
      
      // 类型检查选项
      check: true,
      
      // 声明文件生成
      declaration: true,
      declarationDir: 'dist/types',
      
      // 排除测试文件
      exclude: ['**/*.test.ts', '**/*.spec.ts']
    })
  ],
  
  build: {
    lib: {
      entry: 'src/index.ts',
      name: 'MyLib',
      formats: ['es', 'cjs', 'umd']
    },
    
    rollupOptions: {
      external: ['vue', 'react'],
      output: {
        globals: {
          vue: 'Vue',
          react: 'React'
        }
      }
    }
  },
  
  // 类型检查脚本
  define: {
    __VERSION__: JSON.stringify(process.env.npm_package_version)
  }
});

esbuild集成

// esbuild.config.ts
import { build } from 'esbuild';
import { promises as fs } from 'fs';

async function buildWithTypes() {
  // JavaScript构建
  await build({
    entryPoints: ['src/index.ts'],
    bundle: true,
    outfile: 'dist/index.js',
    format: 'esm',
    target: 'es2020',
    
    // TypeScript支持
    loader: {
      '.ts': 'ts',
      '.tsx': 'tsx'
    },
    
    // 外部依赖
    external: ['react', 'react-dom']
  });
  
  // 单独的类型生成
  const tsc = spawn('tsc', [
    '--declaration',
    '--emitDeclarationOnly',
    '--outDir',
    'dist/types'
  ]);
  
  await new Promise((resolve, reject) => {
    tsc.on('close', (code) => {
      if (code === 0) resolve(void 0);
      else reject(new Error(`tsc exited with code ${code}`));
    });
  });
}

buildWithTypes().catch(console.error);

最佳实践

模块组织策略

// 功能模块结构
// src/modules/user/
//   ├── types.ts          // 类型定义
//   ├── service.ts        // 业务逻辑
//   ├── api.ts           // API调用
//   ├── store.ts         // 状态管理
//   └── index.ts         // 模块导出

// types.ts
export interface User {
  id: number;
  name: string;
  email: string;
  roles: Role[];
}

export interface CreateUserRequest {
  name: string;
  email: string;
  password: string;
}

export type UserRole = 'admin' | 'user' | 'guest';

// service.ts
import type { User, CreateUserRequest } from './types';
import { userApi } from './api';

export class UserService {
  async getUser(id: number): Promise<User> {
    return userApi.get(id);
  }
  
  async createUser(data: CreateUserRequest): Promise<User> {
    return userApi.create(data);
  }
}

export const userService = new UserService();

// index.ts - 统一导出
export type * from './types';
export { UserService, userService } from './service';
export { userApi } from './api';

类型安全的配置

// config.ts
interface DatabaseConfig {
  host: string;
  port: number;
  username: string;
  password: string;
  database: string;
}

interface ApiConfig {
  baseURL: string;
  timeout: number;
  retries: number;
}

interface AppConfig {
  database: DatabaseConfig;
  api: ApiConfig;
  features: {
    [K in FeatureFlag]: boolean;
  };
}

type FeatureFlag = 'userManagement' | 'analytics' | 'notifications';

// 配置验证
export function validateConfig(config: unknown): AppConfig {
  // 运行时类型检查
  if (!isObject(config)) {
    throw new Error('Config must be an object');
  }
  
  // 详细验证逻辑...
  return config as AppConfig;
}

// 环境特定配置
export const config: AppConfig = validateConfig({
  database: {
    host: process.env.DB_HOST || 'localhost',
    port: parseInt(process.env.DB_PORT || '5432'),
    username: process.env.DB_USER || 'postgres',
    password: process.env.DB_PASS || '',
    database: process.env.DB_NAME || 'app'
  },
  api: {
    baseURL: process.env.API_URL || 'http://localhost:3000',
    timeout: 5000,
    retries: 3
  },
  features: {
    userManagement: true,
    analytics: process.env.NODE_ENV === 'production',
    notifications: true
  }
});

依赖注入模式

// di-container.ts
type Constructor<T = {}> = new (...args: any[]) => T;
type Token<T> = Constructor<T> | string | symbol;

export class DIContainer {
  private services = new Map<Token<any>, any>();
  private singletons = new Map<Token<any>, any>();
  
  register<T>(token: Token<T>, implementation: Constructor<T>): void {
    this.services.set(token, implementation);
  }
  
  registerSingleton<T>(token: Token<T>, implementation: Constructor<T>): void {
    this.services.set(token, implementation);
    this.singletons.set(token, null);
  }
  
  resolve<T>(token: Token<T>): T {
    if (this.singletons.has(token)) {
      let instance = this.singletons.get(token);
      if (!instance) {
        instance = this.createInstance(token);
        this.singletons.set(token, instance);
      }
      return instance;
    }
    
    return this.createInstance(token);
  }
  
  private createInstance<T>(token: Token<T>): T {
    const implementation = this.services.get(token);
    if (!implementation) {
      throw new Error(`Service ${String(token)} not found`);
    }
    
    // 获取构造函数参数类型
    const dependencies = this.getDependencies(implementation);
    const resolvedDependencies = dependencies.map(dep => this.resolve(dep));
    
    return new implementation(...resolvedDependencies);
  }
  
  private getDependencies(constructor: Constructor): Token<any>[] {
    // 使用reflect-metadata获取依赖
    return Reflect.getMetadata('design:paramtypes', constructor) || [];
  }
}

// 使用装饰器简化注入
export function Injectable<T extends Constructor>(constructor: T) {
  return constructor;
}

export function Inject(token: Token<any>) {
  return function (target: any, propertyKey: string | symbol | undefined, parameterIndex: number) {
    const existingTokens = Reflect.getMetadata('design:paramtypes', target) || [];
    existingTokens[parameterIndex] = token;
    Reflect.defineMetadata('design:paramtypes', existingTokens, target);
  };
}

// 使用示例
@Injectable
export class UserService {
  constructor(
    @Inject('DATABASE') private db: Database,
    @Inject('LOGGER') private logger: Logger
  ) {}
}

TypeScript为JavaScript模块化开发带来了强大的类型系统支持,不仅提高了代码质量和开发效率,还为大型项目的模块化架构提供了坚实的基础。掌握TypeScript的模块系统和最佳实践,是现代前端开发的必备技能。


下一章: 运行环境差异