vscode-extension-refactorer

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

VS Code Extension Refactorer

VS Code扩展代码重构指南

Overview

概述

This skill enables systematic and safe refactoring of VS Code extension code. It provides structured workflows for identifying improvement opportunities, applying proven refactoring techniques, and ensuring code quality while maintaining functionality.
本技能支持对VS Code扩展代码进行系统化、安全的重构。它提供结构化工作流,用于识别改进机会、应用成熟的重构技术,同时在维持功能的前提下保障代码质量。

When to Use This Skill

适用场景

  • Extracting classes, methods, or modules from large files
  • Reducing code duplication across the codebase
  • Improving TypeScript type safety and interfaces
  • Reorganizing module structure and dependencies
  • Applying design patterns (Manager, Coordinator, Factory, etc.)
  • Optimizing performance-critical code paths
  • Preparing code for new feature development
  • Cleaning up after rapid prototyping
  • 从大文件中提取类、方法或模块
  • 减少代码库中的重复代码
  • 提升TypeScript类型安全性与接口设计
  • 重组模块结构与依赖关系
  • 应用设计模式(Manager、Coordinator、Factory等)
  • 优化性能关键代码路径
  • 为新功能开发准备代码
  • 快速原型开发后的代码清理

Refactoring Workflow

重构工作流

Phase 1: Analysis and Planning

阶段1:分析与规划

Code Smell Detection

代码异味检测

SmellIndicatorsRefactoring
Long Method>50 lines, multiple responsibilitiesExtract Method
Large Class>500 lines, >10 public methodsExtract Class
Duplicate CodeSimilar blocks in 2+ placesExtract Function/Class
Feature EnvyMethod uses other class data extensivelyMove Method
Data ClumpsSame parameters passed togetherIntroduce Parameter Object
Primitive ObsessionOveruse of primitives for domain conceptsReplace with Value Object
Long Parameter List>4 parametersIntroduce Parameter Object
Divergent ChangeClass changes for multiple reasonsExtract Class (SRP)
Shotgun SurgeryOne change requires many file editsMove Method, Inline Class
God ClassClass knows/does too muchExtract Class, Delegate
异味类型识别特征重构方案
过长方法超过50行,承担多个职责提取方法
过大类超过500行,包含10个以上公共方法提取类
重复代码2处及以上存在相似代码块提取函数/类
特性依恋方法大量使用其他类的数据移动方法
数据泥团相同参数被一起传递引入参数对象
基本类型偏执过度使用基本类型表示领域概念替换为值对象
过长参数列表参数数量超过4个引入参数对象
发散式变化类因多种原因发生变更提取类(单一职责原则)
霰弹式修改一处变更需要修改多个文件移动方法、内联类
上帝类类承担过多职责、知晓过多细节提取类、委托职责

Impact Assessment

影响评估

typescript
// Before refactoring, assess:
interface RefactoringAssessment {
  // Scope
  filesAffected: string[];
  publicApiChanges: boolean;
  breakingChanges: boolean;

  // Risk
  testCoverage: 'high' | 'medium' | 'low';
  complexity: 'high' | 'medium' | 'low';

  // Dependencies
  internalDependents: string[];
  externalDependents: string[];  // Other extensions, APIs
}
typescript
// Before refactoring, assess:
interface RefactoringAssessment {
  // Scope
  filesAffected: string[];
  publicApiChanges: boolean;
  breakingChanges: boolean;

  // Risk
  testCoverage: 'high' | 'medium' | 'low';
  complexity: 'high' | 'medium' | 'low';

  // Dependencies
  internalDependents: string[];
  externalDependents: string[];  // Other extensions, APIs
}

Phase 2: Safe Refactoring Techniques

阶段2:安全重构技术

Extract Method

提取方法

Before:
typescript
async function processTerminals(): Promise<void> {
  // Validation logic (10 lines)
  if (!this.terminals) {
    throw new Error('Terminals not initialized');
  }
  if (this.terminals.size === 0) {
    console.log('No terminals to process');
    return;
  }

  // Processing logic (20 lines)
  for (const [id, terminal] of this.terminals) {
    const state = terminal.getState();
    if (state === 'running') {
      await terminal.sendCommand('status');
      const output = await terminal.waitForOutput();
      this.results.set(id, output);
    }
  }

  // Cleanup logic (10 lines)
  this.results.forEach((result, id) => {
    if (result.includes('error')) {
      this.terminals.get(id)?.restart();
    }
  });
}
After:
typescript
async function processTerminals(): Promise<void> {
  this.validateTerminals();
  await this.collectTerminalStatuses();
  this.handleErrorResults();
}

private validateTerminals(): void {
  if (!this.terminals) {
    throw new Error('Terminals not initialized');
  }
  if (this.terminals.size === 0) {
    console.log('No terminals to process');
    return;
  }
}

private async collectTerminalStatuses(): Promise<void> {
  for (const [id, terminal] of this.terminals) {
    const state = terminal.getState();
    if (state === 'running') {
      await terminal.sendCommand('status');
      const output = await terminal.waitForOutput();
      this.results.set(id, output);
    }
  }
}

private handleErrorResults(): void {
  this.results.forEach((result, id) => {
    if (result.includes('error')) {
      this.terminals.get(id)?.restart();
    }
  });
}
Before:
typescript
async function processTerminals(): Promise<void> {
  // Validation logic (10 lines)
  if (!this.terminals) {
    throw new Error('Terminals not initialized');
  }
  if (this.terminals.size === 0) {
    console.log('No terminals to process');
    return;
  }

  // Processing logic (20 lines)
  for (const [id, terminal] of this.terminals) {
    const state = terminal.getState();
    if (state === 'running') {
      await terminal.sendCommand('status');
      const output = await terminal.waitForOutput();
      this.results.set(id, output);
    }
  }

  // Cleanup logic (10 lines)
  this.results.forEach((result, id) => {
    if (result.includes('error')) {
      this.terminals.get(id)?.restart();
    }
  });
}
After:
typescript
async function processTerminals(): Promise<void> {
  this.validateTerminals();
  await this.collectTerminalStatuses();
  this.handleErrorResults();
}

private validateTerminals(): void {
  if (!this.terminals) {
    throw new Error('Terminals not initialized');
  }
  if (this.terminals.size === 0) {
    console.log('No terminals to process');
    return;
  }
}

private async collectTerminalStatuses(): Promise<void> {
  for (const [id, terminal] of this.terminals) {
    const state = terminal.getState();
    if (state === 'running') {
      await terminal.sendCommand('status');
      const output = await terminal.waitForOutput();
      this.results.set(id, output);
    }
  }
}

private handleErrorResults(): void {
  this.results.forEach((result, id) => {
    if (result.includes('error')) {
      this.terminals.get(id)?.restart();
    }
  });
}

Extract Class

提取类

Before (God Class):
typescript
class TerminalManager {
  // Terminal management (proper responsibility)
  private terminals: Map<number, Terminal>;
  createTerminal(): Terminal { }
  disposeTerminal(id: number): void { }

  // UI concerns (wrong place)
  private statusBarItem: vscode.StatusBarItem;
  updateStatusBar(): void { }
  showNotification(msg: string): void { }

  // Persistence concerns (wrong place)
  saveState(): void { }
  loadState(): void { }
  migrateOldState(): void { }

  // Configuration concerns (wrong place)
  getConfig(key: string): unknown { }
  updateConfig(key: string, value: unknown): void { }
}
After (Single Responsibility):
typescript
// Core terminal management
class TerminalManager {
  private terminals: Map<number, Terminal>;

  constructor(
    private ui: TerminalUIManager,
    private persistence: TerminalPersistence,
    private config: TerminalConfig
  ) {}

  createTerminal(): Terminal {
    const terminal = new Terminal(this.config.getShellPath());
    this.terminals.set(terminal.id, terminal);
    this.ui.updateStatusBar(this.terminals.size);
    this.persistence.saveState(this.getState());
    return terminal;
  }

  disposeTerminal(id: number): void {
    this.terminals.delete(id);
    this.ui.updateStatusBar(this.terminals.size);
    this.persistence.saveState(this.getState());
  }
}

// UI responsibility
class TerminalUIManager {
  private statusBarItem: vscode.StatusBarItem;

  updateStatusBar(count: number): void { }
  showNotification(msg: string): void { }
}

// Persistence responsibility
class TerminalPersistence {
  saveState(state: TerminalState): void { }
  loadState(): TerminalState { }
  migrateOldState(): void { }
}

// Configuration responsibility
class TerminalConfig {
  getShellPath(): string { }
  get(key: string): unknown { }
  update(key: string, value: unknown): void { }
}
Before (上帝类):
typescript
class TerminalManager {
  // Terminal management (proper responsibility)
  private terminals: Map<number, Terminal>;
  createTerminal(): Terminal { }
  disposeTerminal(id: number): void { }

  // UI concerns (wrong place)
  private statusBarItem: vscode.StatusBarItem;
  updateStatusBar(): void { }
  showNotification(msg: string): void { }

  // Persistence concerns (wrong place)
  saveState(): void { }
  loadState(): void { }
  migrateOldState(): void { }

  // Configuration concerns (wrong place)
  getConfig(key: string): unknown { }
  updateConfig(key: string, value: unknown): void { }
}
After (单一职责):
typescript
// Core terminal management
class TerminalManager {
  private terminals: Map<number, Terminal>;

  constructor(
    private ui: TerminalUIManager,
    private persistence: TerminalPersistence,
    private config: TerminalConfig
  ) {}

  createTerminal(): Terminal {
    const terminal = new Terminal(this.config.getShellPath());
    this.terminals.set(terminal.id, terminal);
    this.ui.updateStatusBar(this.terminals.size);
    this.persistence.saveState(this.getState());
    return terminal;
  }

  disposeTerminal(id: number): void {
    this.terminals.delete(id);
    this.ui.updateStatusBar(this.terminals.size);
    this.persistence.saveState(this.getState());
  }
}

// UI responsibility
class TerminalUIManager {
  private statusBarItem: vscode.StatusBarItem;

  updateStatusBar(count: number): void { }
  showNotification(msg: string): void { }
}

// Persistence responsibility
class TerminalPersistence {
  saveState(state: TerminalState): void { }
  loadState(): TerminalState { }
  migrateOldState(): void { }
}

// Configuration responsibility
class TerminalConfig {
  getShellPath(): string { }
  get(key: string): unknown { }
  update(key: string, value: unknown): void { }
}

Replace Conditional with Polymorphism

用多态替代条件判断

Before:
typescript
function handleMessage(message: Message): void {
  switch (message.type) {
    case 'create':
      const terminal = createTerminal(message.config);
      sendResponse({ type: 'created', id: terminal.id });
      break;
    case 'write':
      const t = getTerminal(message.id);
      if (t) t.write(message.data);
      break;
    case 'resize':
      const term = getTerminal(message.id);
      if (term) term.resize(message.cols, message.rows);
      break;
    case 'dispose':
      disposeTerminal(message.id);
      sendResponse({ type: 'disposed', id: message.id });
      break;
    default:
      console.warn('Unknown message type:', message.type);
  }
}
After:
typescript
interface MessageHandler {
  handle(message: Message): void;
}

class CreateHandler implements MessageHandler {
  handle(message: CreateMessage): void {
    const terminal = this.manager.createTerminal(message.config);
    this.sender.send({ type: 'created', id: terminal.id });
  }
}

class WriteHandler implements MessageHandler {
  handle(message: WriteMessage): void {
    this.manager.getTerminal(message.id)?.write(message.data);
  }
}

class ResizeHandler implements MessageHandler {
  handle(message: ResizeMessage): void {
    this.manager.getTerminal(message.id)?.resize(message.cols, message.rows);
  }
}

class DisposeHandler implements MessageHandler {
  handle(message: DisposeMessage): void {
    this.manager.disposeTerminal(message.id);
    this.sender.send({ type: 'disposed', id: message.id });
  }
}

// Message router
class MessageRouter {
  private handlers = new Map<string, MessageHandler>([
    ['create', new CreateHandler()],
    ['write', new WriteHandler()],
    ['resize', new ResizeHandler()],
    ['dispose', new DisposeHandler()]
  ]);

  route(message: Message): void {
    const handler = this.handlers.get(message.type);
    if (handler) {
      handler.handle(message);
    } else {
      console.warn('Unknown message type:', message.type);
    }
  }
}
Before:
typescript
function handleMessage(message: Message): void {
  switch (message.type) {
    case 'create':
      const terminal = createTerminal(message.config);
      sendResponse({ type: 'created', id: terminal.id });
      break;
    case 'write':
      const t = getTerminal(message.id);
      if (t) t.write(message.data);
      break;
    case 'resize':
      const term = getTerminal(message.id);
      if (term) term.resize(message.cols, message.rows);
      break;
    case 'dispose':
      disposeTerminal(message.id);
      sendResponse({ type: 'disposed', id: message.id });
      break;
    default:
      console.warn('Unknown message type:', message.type);
  }
}
After:
typescript
interface MessageHandler {
  handle(message: Message): void;
}

class CreateHandler implements MessageHandler {
  handle(message: CreateMessage): void {
    const terminal = this.manager.createTerminal(message.config);
    this.sender.send({ type: 'created', id: terminal.id });
  }
}

class WriteHandler implements MessageHandler {
  handle(message: WriteMessage): void {
    this.manager.getTerminal(message.id)?.write(message.data);
  }
}

class ResizeHandler implements MessageHandler {
  handle(message: ResizeMessage): void {
    this.manager.getTerminal(message.id)?.resize(message.cols, message.rows);
  }
}

class DisposeHandler implements MessageHandler {
  handle(message: DisposeMessage): void {
    this.manager.disposeTerminal(message.id);
    this.sender.send({ type: 'disposed', id: message.id });
  }
}

// Message router
class MessageRouter {
  private handlers = new Map<string, MessageHandler>([
    ['create', new CreateHandler()],
    ['write', new WriteHandler()],
    ['resize', new ResizeHandler()],
    ['dispose', new DisposeHandler()]
  ]);

  route(message: Message): void {
    const handler = this.handlers.get(message.type);
    if (handler) {
      handler.handle(message);
    } else {
      console.warn('Unknown message type:', message.type);
    }
  }
}

Introduce Parameter Object

引入参数对象

Before:
typescript
function createTerminal(
  shell: string,
  cwd: string,
  env: Record<string, string>,
  cols: number,
  rows: number,
  scrollback: number,
  name?: string
): Terminal {
  // ...
}

// Caller must remember order
createTerminal('/bin/bash', '/home', {}, 80, 24, 1000, 'Main');
After:
typescript
interface TerminalOptions {
  shell: string;
  cwd: string;
  env?: Record<string, string>;
  dimensions?: {
    cols: number;
    rows: number;
  };
  scrollback?: number;
  name?: string;
}

const DEFAULT_OPTIONS: Partial<TerminalOptions> = {
  env: {},
  dimensions: { cols: 80, rows: 24 },
  scrollback: 1000
};

function createTerminal(options: TerminalOptions): Terminal {
  const opts = { ...DEFAULT_OPTIONS, ...options };
  // ...
}

// Clear, self-documenting call
createTerminal({
  shell: '/bin/bash',
  cwd: '/home',
  name: 'Main'
});
Before:
typescript
function createTerminal(
  shell: string,
  cwd: string,
  env: Record<string, string>,
  cols: number,
  rows: number,
  scrollback: number,
  name?: string
): Terminal {
  // ...
}

// Caller must remember order
createTerminal('/bin/bash', '/home', {}, 80, 24, 1000, 'Main');
After:
typescript
interface TerminalOptions {
  shell: string;
  cwd: string;
  env?: Record<string, string>;
  dimensions?: {
    cols: number;
    rows: number;
  };
  scrollback?: number;
  name?: string;
}

const DEFAULT_OPTIONS: Partial<TerminalOptions> = {
  env: {},
  dimensions: { cols: 80, rows: 24 },
  scrollback: 1000
};

function createTerminal(options: TerminalOptions): Terminal {
  const opts = { ...DEFAULT_OPTIONS, ...options };
  // ...
}

// Clear, self-documenting call
createTerminal({
  shell: '/bin/bash',
  cwd: '/home',
  name: 'Main'
});

Phase 3: VS Code-Specific Patterns

阶段3:VS Code专属模式

Manager-Coordinator Pattern

管理者-协调者模式

Standard pattern for VS Code extensions with multiple concerns:
typescript
// Coordinator orchestrates managers
class TerminalWebviewManager implements ICoordinator {
  private managers: Map<string, IManager> = new Map();

  constructor(context: vscode.ExtensionContext) {
    // Initialize managers
    this.managers.set('message', new MessageManager(this));
    this.managers.set('ui', new UIManager(this));
    this.managers.set('input', new InputManager(this));
    this.managers.set('performance', new PerformanceManager(this));
  }

  getManager<T extends IManager>(name: string): T {
    return this.managers.get(name) as T;
  }

  async initialize(): Promise<void> {
    for (const manager of this.managers.values()) {
      await manager.initialize();
    }
  }

  dispose(): void {
    // Dispose in reverse order
    const managers = Array.from(this.managers.values()).reverse();
    for (const manager of managers) {
      manager.dispose();
    }
  }
}

// Individual manager interface
interface IManager extends vscode.Disposable {
  initialize(): Promise<void>;
}

// Example manager implementation
class MessageManager implements IManager {
  constructor(private coordinator: ICoordinator) {}

  async initialize(): Promise<void> {
    // Setup message handling
  }

  dispose(): void {
    // Cleanup
  }
}
适用于包含多类职责的VS Code扩展的标准模式:
typescript
// Coordinator orchestrates managers
class TerminalWebviewManager implements ICoordinator {
  private managers: Map<string, IManager> = new Map();

  constructor(context: vscode.ExtensionContext) {
    // Initialize managers
    this.managers.set('message', new MessageManager(this));
    this.managers.set('ui', new UIManager(this));
    this.managers.set('input', new InputManager(this));
    this.managers.set('performance', new PerformanceManager(this));
  }

  getManager<T extends IManager>(name: string): T {
    return this.managers.get(name) as T;
  }

  async initialize(): Promise<void> {
    for (const manager of this.managers.values()) {
      await manager.initialize();
    }
  }

  dispose(): void {
    // Dispose in reverse order
    const managers = Array.from(this.managers.values()).reverse();
    for (const manager of managers) {
      manager.dispose();
    }
  }
}

// Individual manager interface
interface IManager extends vscode.Disposable {
  initialize(): Promise<void>;
}

// Example manager implementation
class MessageManager implements IManager {
  constructor(private coordinator: ICoordinator) {}

  async initialize(): Promise<void> {
    // Setup message handling
  }

  dispose(): void {
    // Cleanup
  }
}

Service Locator Pattern

服务定位器模式

For complex dependency management:
typescript
class ServiceContainer {
  private services = new Map<string, unknown>();
  private factories = new Map<string, () => unknown>();

  register<T>(key: string, instance: T): void {
    this.services.set(key, instance);
  }

  registerFactory<T>(key: string, factory: () => T): void {
    this.factories.set(key, factory);
  }

  get<T>(key: string): T {
    if (this.services.has(key)) {
      return this.services.get(key) as T;
    }

    const factory = this.factories.get(key);
    if (factory) {
      const instance = factory() as T;
      this.services.set(key, instance);
      return instance;
    }

    throw new Error(`Service not found: ${key}`);
  }
}

// Usage
const container = new ServiceContainer();
container.register('config', new ConfigService());
container.registerFactory('terminal', () => new TerminalService(
  container.get('config')
));
用于复杂依赖管理:
typescript
class ServiceContainer {
  private services = new Map<string, unknown>();
  private factories = new Map<string, () => unknown>();

  register<T>(key: string, instance: T): void {
    this.services.set(key, instance);
  }

  registerFactory<T>(key: string, factory: () => T): void {
    this.factories.set(key, factory);
  }

  get<T>(key: string): T {
    if (this.services.has(key)) {
      return this.services.get(key) as T;
    }

    const factory = this.factories.get(key);
    if (factory) {
      const instance = factory() as T;
      this.services.set(key, instance);
      return instance;
    }

    throw new Error(`Service not found: ${key}`);
  }
}

// Usage
const container = new ServiceContainer();
container.register('config', new ConfigService());
container.registerFactory('terminal', () => new TerminalService(
  container.get('config')
));

Disposable Chain Pattern

可释放链模式

Proper resource cleanup:
typescript
class DisposableChain implements vscode.Disposable {
  private chain: vscode.Disposable[] = [];

  add<T extends vscode.Disposable>(disposable: T): T {
    this.chain.push(disposable);
    return disposable;
  }

  addFunction(fn: () => void): void {
    this.chain.push({ dispose: fn });
  }

  dispose(): void {
    // LIFO disposal
    while (this.chain.length) {
      const d = this.chain.pop();
      try {
        d?.dispose();
      } catch (e) {
        console.error('Dispose error:', e);
      }
    }
  }
}

// Usage in extension
export function activate(context: vscode.ExtensionContext) {
  const disposables = new DisposableChain();

  const config = disposables.add(new ConfigService());
  const terminal = disposables.add(new TerminalService(config));
  const ui = disposables.add(new UIService(terminal));

  context.subscriptions.push(disposables);
}
用于正确的资源清理:
typescript
class DisposableChain implements vscode.Disposable {
  private chain: vscode.Disposable[] = [];

  add<T extends vscode.Disposable>(disposable: T): T {
    this.chain.push(disposable);
    return disposable;
  }

  addFunction(fn: () => void): void {
    this.chain.push({ dispose: fn });
  }

  dispose(): void {
    // LIFO disposal
    while (this.chain.length) {
      const d = this.chain.pop();
      try {
        d?.dispose();
      } catch (e) {
        console.error('Dispose error:', e);
      }
    }
  }
}

// Usage in extension
export function activate(context: vscode.ExtensionContext) {
  const disposables = new DisposableChain();

  const config = disposables.add(new ConfigService());
  const terminal = disposables.add(new TerminalService(config));
  const ui = disposables.add(new UIService(terminal));

  context.subscriptions.push(disposables);
}

Phase 4: Type Safety Improvements

阶段4:类型安全性提升

Replace
any
with Proper Types

用合适类型替换
any

Before:
typescript
function processMessage(message: any): any {
  if (message.type === 'data') {
    return message.payload.items.map((item: any) => item.value);
  }
  return null;
}
After:
typescript
interface DataMessage {
  type: 'data';
  payload: {
    items: Array<{ value: string }>;
  };
}

interface ErrorMessage {
  type: 'error';
  error: string;
}

type Message = DataMessage | ErrorMessage;

function processMessage(message: Message): string[] | null {
  if (message.type === 'data') {
    return message.payload.items.map(item => item.value);
  }
  return null;
}
Before:
typescript
function processMessage(message: any): any {
  if (message.type === 'data') {
    return message.payload.items.map((item: any) => item.value);
  }
  return null;
}
After:
typescript
interface DataMessage {
  type: 'data';
  payload: {
    items: Array<{ value: string }>;
  };
}

interface ErrorMessage {
  type: 'error';
  error: string;
}

type Message = DataMessage | ErrorMessage;

function processMessage(message: Message): string[] | null {
  if (message.type === 'data') {
    return message.payload.items.map(item => item.value);
  }
  return null;
}

Discriminated Unions

可辨识联合类型

Before:
typescript
interface TerminalState {
  status: string;
  data?: string;
  error?: Error;
  progress?: number;
}

// Caller must check multiple fields
function render(state: TerminalState): void {
  if (state.status === 'loading' && state.progress !== undefined) {
    showProgress(state.progress);
  } else if (state.status === 'success' && state.data) {
    showData(state.data);
  } else if (state.status === 'error' && state.error) {
    showError(state.error);
  }
}
After:
typescript
type TerminalState =
  | { status: 'idle' }
  | { status: 'loading'; progress: number }
  | { status: 'success'; data: string }
  | { status: 'error'; error: Error };

// Type narrowing with exhaustive check
function render(state: TerminalState): void {
  switch (state.status) {
    case 'idle':
      showIdle();
      break;
    case 'loading':
      showProgress(state.progress); // progress guaranteed
      break;
    case 'success':
      showData(state.data); // data guaranteed
      break;
    case 'error':
      showError(state.error); // error guaranteed
      break;
    default:
      const _exhaustive: never = state;
      throw new Error(`Unhandled state: ${_exhaustive}`);
  }
}
Before:
typescript
interface TerminalState {
  status: string;
  data?: string;
  error?: Error;
  progress?: number;
}

// Caller must check multiple fields
function render(state: TerminalState): void {
  if (state.status === 'loading' && state.progress !== undefined) {
    showProgress(state.progress);
  } else if (state.status === 'success' && state.data) {
    showData(state.data);
  } else if (state.status === 'error' && state.error) {
    showError(state.error);
  }
}
After:
typescript
type TerminalState =
  | { status: 'idle' }
  | { status: 'loading'; progress: number }
  | { status: 'success'; data: string }
  | { status: 'error'; error: Error };

// Type narrowing with exhaustive check
function render(state: TerminalState): void {
  switch (state.status) {
    case 'idle':
      showIdle();
      break;
    case 'loading':
      showProgress(state.progress); // progress guaranteed
      break;
    case 'success':
      showData(state.data); // data guaranteed
      break;
    case 'error':
      showError(state.error); // error guaranteed
      break;
    default:
      const _exhaustive: never = state;
      throw new Error(`Unhandled state: ${_exhaustive}`);
  }
}

Generic Constraints

泛型约束

Before:
typescript
class Repository<T> {
  private items: T[] = [];

  add(item: T): void {
    this.items.push(item);
  }

  findById(id: number): T | undefined {
    // Can't access .id because T is unconstrained
    return this.items.find((item: any) => item.id === id);
  }
}
After:
typescript
interface Identifiable {
  id: number;
}

class Repository<T extends Identifiable> {
  private items: T[] = [];

  add(item: T): void {
    this.items.push(item);
  }

  findById(id: number): T | undefined {
    return this.items.find(item => item.id === id); // Type-safe
  }

  update(id: number, updates: Partial<Omit<T, 'id'>>): boolean {
    const item = this.findById(id);
    if (item) {
      Object.assign(item, updates);
      return true;
    }
    return false;
  }
}
Before:
typescript
class Repository<T> {
  private items: T[] = [];

  add(item: T): void {
    this.items.push(item);
  }

  findById(id: number): T | undefined {
    // Can't access .id because T is unconstrained
    return this.items.find((item: any) => item.id === id);
  }
}
After:
typescript
interface Identifiable {
  id: number;
}

class Repository<T extends Identifiable> {
  private items: T[] = [];

  add(item: T): void {
    this.items.push(item);
  }

  findById(id: number): T | undefined {
    return this.items.find(item => item.id === id); // Type-safe
  }

  update(id: number, updates: Partial<Omit<T, 'id'>>): boolean {
    const item = this.findById(id);
    if (item) {
      Object.assign(item, updates);
      return true;
    }
    return false;
  }
}

Phase 5: Verification

阶段5:验证

Refactoring Checklist

重构检查清单

  • All tests pass after refactoring
  • No new TypeScript errors introduced
  • Public API maintained (or documented changes)
  • Performance not degraded
  • Memory usage stable
  • No circular dependencies introduced
  • Documentation updated if needed
  • 重构后所有测试通过
  • 未引入新的TypeScript错误
  • 公共API保持兼容(或已记录变更)
  • 性能未出现下降
  • 内存使用稳定
  • 未引入循环依赖
  • 必要时已更新文档

Testing Strategy

测试策略

typescript
// Before refactoring: Capture behavior
describe('TerminalManager (before refactor)', () => {
  it('creates terminal with correct config', () => {
    const result = manager.createTerminal(config);
    expect(result).toMatchSnapshot();
  });
});

// After refactoring: Same tests must pass
describe('TerminalManager (after refactor)', () => {
  it('creates terminal with correct config', () => {
    const result = manager.createTerminal(config);
    expect(result).toMatchSnapshot(); // Same snapshot
  });
});
typescript
// Before refactoring: Capture behavior
describe('TerminalManager (before refactor)', () => {
  it('creates terminal with correct config', () => {
    const result = manager.createTerminal(config);
    expect(result).toMatchSnapshot();
  });
});

// After refactoring: Same tests must pass
describe('TerminalManager (after refactor)', () => {
  it('creates terminal with correct config', () => {
    const result = manager.createTerminal(config);
    expect(result).toMatchSnapshot(); // Same snapshot
  });
});

Common Refactoring Scenarios

常见重构场景

Scenario 1: Breaking Up a Large File

场景1:拆分大文件

src/terminal.ts (2000 lines)
↓ Split by responsibility
src/terminal/
├── index.ts           # Public exports
├── TerminalManager.ts # Core management
├── TerminalFactory.ts # Creation logic
├── TerminalState.ts   # State management
├── types.ts           # Interfaces and types
└── utils.ts           # Helper functions
src/terminal.ts (2000 lines)
↓ Split by responsibility
src/terminal/
├── index.ts           # Public exports
├── TerminalManager.ts # Core management
├── TerminalFactory.ts # Creation logic
├── TerminalState.ts   # State management
├── types.ts           # Interfaces and types
└── utils.ts           # Helper functions

Scenario 2: Reducing Coupling

场景2:降低耦合度

typescript
// Before: Tight coupling
class TerminalManager {
  private ui = new UIManager();  // Direct instantiation
  private config = new ConfigManager();
}

// After: Dependency injection
class TerminalManager {
  constructor(
    private ui: IUIManager,
    private config: IConfigManager
  ) {}
}

// Factory handles wiring
function createTerminalManager(): TerminalManager {
  return new TerminalManager(
    new UIManager(),
    new ConfigManager()
  );
}
typescript
// Before: Tight coupling
class TerminalManager {
  private ui = new UIManager();  // Direct instantiation
  private config = new ConfigManager();
}

// After: Dependency injection
class TerminalManager {
  constructor(
    private ui: IUIManager,
    private config: IConfigManager
  ) {}
}

// Factory handles wiring
function createTerminalManager(): TerminalManager {
  return new TerminalManager(
    new UIManager(),
    new ConfigManager()
  );
}

Scenario 3: Async Refactoring

场景3:异步代码重构

typescript
// Before: Callback hell
function loadData(callback: (data: Data) => void): void {
  readFile(path, (err, content) => {
    if (err) throw err;
    parseData(content, (err, parsed) => {
      if (err) throw err;
      validateData(parsed, (err, valid) => {
        if (err) throw err;
        callback(valid);
      });
    });
  });
}

// After: Async/await
async function loadData(): Promise<Data> {
  const content = await readFile(path);
  const parsed = await parseData(content);
  return validateData(parsed);
}
typescript
// Before: Callback hell
function loadData(callback: (data: Data) => void): void {
  readFile(path, (err, content) => {
    if (err) throw err;
    parseData(content, (err, parsed) => {
      if (err) throw err;
      validateData(parsed, (err, valid) => {
        if (err) throw err;
        callback(valid);
      });
    });
  });
}

// After: Async/await
async function loadData(): Promise<Data> {
  const content = await readFile(path);
  const parsed = await parseData(content);
  return validateData(parsed);
}

Resources

参考资源

For detailed reference documentation:
  • references/code-smells.md
    - Complete code smell catalog with examples
  • references/design-patterns.md
    - VS Code-specific design pattern implementations
  • references/type-patterns.md
    - Advanced TypeScript type patterns
如需详细参考文档:
  • references/code-smells.md
    - 完整的代码异味目录及示例
  • references/design-patterns.md
    - VS Code专属设计模式实现方案
  • references/type-patterns.md
    - 高级TypeScript类型模式