# TypeScript Architecture & Migration Plan for Chess Game **Author:** System Architecture Designer **Date:** 2025-11-23 **Issue:** #6 - Convert JavaScript to TypeScript **Status:** Architecture Design Document ## Executive Summary This document outlines the comprehensive architecture for migrating the chess game from JavaScript ES6 modules (~3,700 lines across 15 modules) to a type-safe TypeScript codebase. The migration adopts a **gradual, incremental approach** with strict type safety as the end goal. **Key Metrics:** - Current: 15 JavaScript ES6 modules, ~3,700 lines - Architecture: MVC-inspired with clear separation of concerns - Migration Strategy: Bottom-up (utilities → models → engine → controllers → views) - Target: Full TypeScript with strict mode enabled - Timeline: 4 phases over estimated 2-3 weeks --- ## 1. Project Structure & Configuration ### 1.1 Directory Structure ``` alex/ ├── src/ # TypeScript source files │ ├── types/ # Type definitions and interfaces │ │ ├── index.ts # Barrel export for all types │ │ ├── core.types.ts # Core game types (Position, Color, etc.) │ │ ├── piece.types.ts # Piece-related types │ │ ├── game.types.ts # Game state types │ │ ├── move.types.ts # Move and validation types │ │ └── ui.types.ts # UI event and rendering types │ ├── pieces/ # Piece classes (TypeScript) │ │ ├── Piece.ts │ │ ├── Pawn.ts │ │ ├── Knight.ts │ │ ├── Bishop.ts │ │ ├── Rook.ts │ │ ├── Queen.ts │ │ └── King.ts │ ├── game/ # Core game logic │ │ ├── Board.ts │ │ └── GameState.ts │ ├── engine/ # Chess engine │ │ ├── MoveValidator.ts │ │ └── SpecialMoves.ts │ ├── controllers/ # Game controllers │ │ ├── GameController.ts │ │ └── DragDropHandler.ts │ ├── views/ # Rendering │ │ └── BoardRenderer.ts │ ├── utils/ # Utilities │ │ ├── Constants.ts │ │ ├── EventBus.ts │ │ └── Helpers.ts │ └── main.ts # Application entry point ├── dist/ # Compiled JavaScript output │ ├── main.js │ └── main.js.map ├── js/ # Legacy JavaScript (during migration) │ └── [existing JS files] ├── tests/ # Test files │ ├── unit/ │ │ ├── pieces/ │ │ ├── game/ │ │ └── engine/ │ └── integration/ ├── docs/ # Documentation ├── tsconfig.json # TypeScript configuration ├── tsconfig.build.json # Production build config ├── jest.config.ts # TypeScript Jest config └── package.json # Project dependencies ``` ### 1.2 TypeScript Configuration **File: `tsconfig.json`** ```json { "compilerOptions": { /* Language and Environment */ "target": "ES2020", "lib": ["ES2020", "DOM", "DOM.Iterable"], "module": "ES2020", "moduleResolution": "node", /* Type Checking */ "strict": true, "noImplicitAny": true, "strictNullChecks": true, "strictFunctionTypes": true, "strictBindCallApply": true, "strictPropertyInitialization": true, "noImplicitThis": true, "alwaysStrict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "noUncheckedIndexedAccess": true, "noImplicitOverride": true, "noPropertyAccessFromIndexSignature": true, /* Module Resolution */ "baseUrl": ".", "paths": { "@/*": ["src/*"], "@types/*": ["src/types/*"], "@pieces/*": ["src/pieces/*"], "@game/*": ["src/game/*"], "@engine/*": ["src/engine/*"], "@controllers/*": ["src/controllers/*"], "@views/*": ["src/views/*"], "@utils/*": ["src/utils/*"] }, "resolveJsonModule": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, /* Emit */ "declaration": true, "declarationMap": true, "sourceMap": true, "outDir": "./dist", "removeComments": false, "importHelpers": true, "downlevelIteration": true, "inlineSources": false, "newLine": "lf", /* Interop Constraints */ "isolatedModules": true, "allowJs": true, "checkJs": false, "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, "include": [ "src/**/*", "tests/**/*" ], "exclude": [ "node_modules", "dist", "js", "coverage" ], "ts-node": { "compilerOptions": { "module": "commonjs" } } } ``` **File: `tsconfig.build.json`** (Production builds) ```json { "extends": "./tsconfig.json", "compilerOptions": { "sourceMap": false, "declaration": false, "removeComments": true, "noUnusedLocals": true, "noUnusedParameters": true }, "exclude": [ "node_modules", "dist", "js", "coverage", "tests", "**/*.test.ts", "**/*.spec.ts" ] } ``` --- ## 2. Type Definition Hierarchy ### 2.1 Core Type Definitions **File: `src/types/core.types.ts`** ```typescript /** * Core game type definitions */ /** Board position (0-7 indexed) */ export interface Position { readonly row: number; readonly col: number; } /** Chess square (position with optional piece) */ export interface Square { position: Position; piece: Piece | null; isLight: boolean; } /** Player color */ export type Color = 'white' | 'black'; /** Algebraic notation (e.g., "e4", "Nf3") */ export type AlgebraicNotation = string; /** FEN notation string */ export type FEN = string; /** PGN notation string */ export type PGN = string; /** File letters (a-h) */ export type File = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h'; /** Rank numbers (1-8) */ export type Rank = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8'; /** Direction vector for piece movement */ export interface Direction { readonly row: -1 | 0 | 1; readonly col: -1 | 0 | 1; } /** Extended direction for knights */ export interface KnightDirection { readonly row: -2 | -1 | 1 | 2; readonly col: -2 | -1 | 1 | 2; } /** Board grid (8x8 array) */ export type BoardGrid = Array>; /** Castling rights */ export interface CastlingRights { whiteKingside: boolean; whiteQueenside: boolean; blackKingside: boolean; blackQueenside: boolean; } /** Game result */ export type GameResult = '1-0' | '0-1' | '1/2-1/2' | '*'; ``` ### 2.2 Piece Type Definitions **File: `src/types/piece.types.ts`** ```typescript import { Color, Position, BoardGrid } from './core.types'; /** Piece type enumeration */ export enum PieceType { PAWN = 'pawn', KNIGHT = 'knight', BISHOP = 'bishop', ROOK = 'rook', QUEEN = 'queen', KING = 'king' } /** Piece value in centipawns */ export type PieceValue = number; /** Piece symbol (Unicode) */ export type PieceSymbol = string; /** FEN character for piece */ export type FENChar = 'K' | 'Q' | 'R' | 'B' | 'N' | 'P' | 'k' | 'q' | 'r' | 'b' | 'n' | 'p'; /** Base piece interface */ export interface IPiece { readonly color: Color; readonly type: PieceType; readonly value: PieceValue; position: Position; hasMoved: boolean; getValidMoves(board: IBoard): Position[]; isValidMove(board: IBoard, toRow: number, toCol: number): boolean; clone(): IPiece; getSymbol(): PieceSymbol; toFENChar(): FENChar; } /** Board interface (to avoid circular dependencies) */ export interface IBoard { readonly grid: BoardGrid; getPiece(row: number, col: number): IPiece | null; setPiece(row: number, col: number, piece: IPiece | null): void; isInBounds(row: number, col: number): boolean; clone(): IBoard; } /** Promotion target pieces */ export type PromotionPiece = PieceType.QUEEN | PieceType.ROOK | PieceType.BISHOP | PieceType.KNIGHT; /** Piece movement pattern */ export interface MovementPattern { directions: Array<{ row: number; col: number }>; sliding: boolean; maxDistance?: number; } ``` ### 2.3 Game State Type Definitions **File: `src/types/game.types.ts`** ```typescript import { Color, Position, CastlingRights, FEN, PGN, GameResult } from './core.types'; import { IPiece } from './piece.types'; import { Move } from './move.types'; /** Game status */ export enum GameStatus { ACTIVE = 'active', CHECK = 'check', CHECKMATE = 'checkmate', STALEMATE = 'stalemate', DRAW = 'draw', RESIGNED = 'resigned' } /** Draw reason */ export enum DrawReason { AGREEMENT = 'agreement', FIFTY_MOVE_RULE = '50-move rule', THREEFOLD_REPETITION = 'threefold repetition', INSUFFICIENT_MATERIAL = 'insufficient material', STALEMATE = 'stalemate' } /** Captured pieces by color */ export interface CapturedPieces { white: IPiece[]; black: IPiece[]; } /** Game state interface */ export interface IGameState { readonly moveHistory: Move[]; readonly capturedPieces: CapturedPieces; currentMove: number; status: GameStatus; enPassantTarget: Position | null; halfMoveClock: number; fullMoveNumber: number; drawOffer: Color | null; recordMove(move: Move): void; getLastMove(): Move | null; undo(): Move | null; redo(): Move | null; isFiftyMoveRule(): boolean; isThreefoldRepetition(currentFEN: FEN): boolean; toFEN(board: IBoard, currentTurn: Color): FEN; toPGN(metadata?: PGNMetadata): PGN; reset(): void; } /** PGN metadata */ export interface PGNMetadata { event?: string; site?: string; date?: string; round?: string; white?: string; black?: string; result?: GameResult; whiteElo?: number; blackElo?: number; timeControl?: string; [key: string]: string | number | undefined; } /** Game configuration */ export interface GameConfig { autoSave?: boolean; enableTimer?: boolean; timeControl?: TimeControl | null; allowUndo?: boolean; highlightLegalMoves?: boolean; } /** Time control */ export interface TimeControl { initial: number; // Initial time in seconds increment: number; // Increment per move in seconds type: 'fischer' | 'bronstein' | 'simple'; } /** Player timer state */ export interface TimerState { white: number; black: number; isRunning: boolean; activeColor: Color | null; } ``` ### 2.4 Move Type Definitions **File: `src/types/move.types.ts`** ```typescript import { Color, Position, AlgebraicNotation, FEN } from './core.types'; import { IPiece, PieceType, PromotionPiece } from './piece.types'; /** Special move types */ export enum SpecialMove { CASTLE_KINGSIDE = 'castle-kingside', CASTLE_QUEENSIDE = 'castle-queenside', EN_PASSANT = 'en-passant', PROMOTION = 'promotion', NORMAL = 'normal' } /** Move object */ export interface Move { readonly from: Position; readonly to: Position; readonly piece: IPiece; readonly captured: IPiece | null; notation: AlgebraicNotation; readonly special: SpecialMove; readonly promotedTo: PieceType | null; readonly timestamp: number; readonly fen: FEN; isCheck?: boolean; isCheckmate?: boolean; } /** Move result (after execution) */ export interface MoveResult { success: boolean; move?: Move; error?: MoveError; gameStatus?: GameStatus; } /** Move validation result */ export interface ValidationResult { valid: boolean; error?: MoveError; warnings?: string[]; } /** Move error types */ export enum MoveError { NO_PIECE = 'No piece at source position', WRONG_TURN = 'Not your turn', INVALID_MOVE = 'Invalid move', KING_IN_CHECK = 'Move leaves king in check', BLOCKED_PATH = 'Path is blocked', OUT_OF_BOUNDS = 'Move out of bounds', FRIENDLY_FIRE = 'Cannot capture own piece', CASTLE_THROUGH_CHECK = 'Cannot castle through check', CASTLE_IN_CHECK = 'Cannot castle while in check', CASTLE_NOT_ALLOWED = 'Castling not allowed' } /** Castling move details */ export interface CastlingMove { kingFrom: Position; kingTo: Position; rookFrom: Position; rookTo: Position; type: SpecialMove.CASTLE_KINGSIDE | SpecialMove.CASTLE_QUEENSIDE; } /** Promotion details */ export interface PromotionDetails { pawn: IPiece; position: Position; newPiece: PromotionPiece; } /** Move generation options */ export interface MoveGenerationOptions { includeSpecialMoves?: boolean; checkLegality?: boolean; forceCalculation?: boolean; } ``` ### 2.5 UI Event Type Definitions **File: `src/types/ui.types.ts`** ```typescript import { Position, Color } from './core.types'; import { IPiece } from './piece.types'; import { Move, PromotionDetails } from './move.types'; import { GameStatus, DrawReason } from './game.types'; /** DOM element IDs */ export enum DOMElementId { BOARD = 'board', MOVE_HISTORY = 'move-history', CAPTURED_WHITE = 'captured-white', CAPTURED_BLACK = 'captured-black', CURRENT_TURN = 'current-turn', GAME_STATUS = 'game-status', NEW_GAME_BTN = 'new-game-btn', UNDO_BTN = 'undo-btn', REDO_BTN = 'redo-btn' } /** Board render options */ export interface RenderOptions { highlightedSquares?: Position[]; selectedSquare?: Position | null; lastMove?: Move | null; checkSquare?: Position | null; orientation?: Color; showCoordinates?: boolean; } /** Drag and drop event data */ export interface DragEventData { piece: IPiece; fromPosition: Position; dragElement: HTMLElement; offsetX: number; offsetY: number; } /** Drop event data */ export interface DropEventData { piece: IPiece; fromPosition: Position; toPosition: Position; isValid: boolean; } /** Game event types */ export enum GameEvent { MOVE = 'move', CAPTURE = 'capture', CHECK = 'check', CHECKMATE = 'checkmate', STALEMATE = 'stalemate', DRAW = 'draw', PROMOTION = 'promotion', CASTLE = 'castle', RESIGN = 'resign', NEW_GAME = 'newgame', UNDO = 'undo', REDO = 'redo', LOAD = 'load', DRAW_OFFERED = 'draw-offered' } /** Game event payloads */ export interface GameEventPayloads { [GameEvent.MOVE]: { move: Move; gameStatus: GameStatus }; [GameEvent.CAPTURE]: { piece: IPiece; position: Position }; [GameEvent.CHECK]: { color: Color }; [GameEvent.CHECKMATE]: { winner: Color }; [GameEvent.STALEMATE]: Record; [GameEvent.DRAW]: { reason: DrawReason }; [GameEvent.PROMOTION]: PromotionDetails; [GameEvent.CASTLE]: { type: SpecialMove; color: Color }; [GameEvent.RESIGN]: { loser: Color }; [GameEvent.NEW_GAME]: Record; [GameEvent.UNDO]: { move: Move }; [GameEvent.REDO]: { move: Move }; [GameEvent.LOAD]: { fen: string; pgn: string }; [GameEvent.DRAW_OFFERED]: { by: Color }; } /** Event handler type */ export type EventHandler = (data: GameEventPayloads[T]) => void; /** Event bus interface */ export interface IEventBus { on(event: T, handler: EventHandler): void; off(event: T, handler: EventHandler): void; emit(event: T, data: GameEventPayloads[T]): void; once(event: T, handler: EventHandler): void; } /** Square element data attributes */ export interface SquareElement extends HTMLElement { dataset: { row: string; col: string; color: 'light' | 'dark'; }; } ``` ### 2.6 Barrel Export **File: `src/types/index.ts`** ```typescript /** * Central type definitions export * Import all types from here: import { Position, Color, Move } from '@types'; */ export * from './core.types'; export * from './piece.types'; export * from './game.types'; export * from './move.types'; export * from './ui.types'; ``` --- ## 3. Migration Order & Phases ### Phase 1: Foundation (Days 1-3) **Priority: Critical Infrastructure** 1. **Setup TypeScript Build System** - Install dependencies: `typescript`, `@types/node`, `@types/jest`, `ts-jest` - Create `tsconfig.json` and `tsconfig.build.json` - Update `package.json` scripts - Configure Jest for TypeScript 2. **Create Type Definitions** - Create all files in `src/types/` - Define all interfaces and types - Export barrel file 3. **Migrate Utilities** (Lowest dependencies) - `src/utils/Constants.ts` ✓ (converts exported constants to enums/types) - `src/utils/Helpers.ts` ✓ (add type signatures) - `src/utils/EventBus.ts` ✓ (use generic types for events) **Validation:** - TypeScript compiles without errors - Type definitions are comprehensive - Utilities have full type coverage ### Phase 2: Core Models (Days 4-7) **Priority: Game Logic Foundation** 4. **Migrate Piece Classes** (Bottom-up hierarchy) - `src/pieces/Piece.ts` ✓ (abstract base class with strict types) - `src/pieces/Pawn.ts` ✓ - `src/pieces/Knight.ts` ✓ - `src/pieces/Bishop.ts` ✓ - `src/pieces/Rook.ts` ✓ - `src/pieces/Queen.ts` ✓ - `src/pieces/King.ts` ✓ 5. **Migrate Game Models** - `src/game/Board.ts` ✓ (use typed grid) - `src/game/GameState.ts` ✓ (strict state management) **Validation:** - All piece classes compile with strict mode - Board operations are type-safe - No implicit `any` types - Test coverage maintained ### Phase 3: Engine & Controllers (Days 8-10) **Priority: Business Logic** 6. **Migrate Chess Engine** - `src/engine/MoveValidator.ts` ✓ (type-safe validation) - `src/engine/SpecialMoves.ts` ✓ (special move handling) 7. **Migrate Controllers** - `src/controllers/GameController.ts` ✓ (orchestration with types) - `src/controllers/DragDropHandler.ts` ✓ (typed DOM events) **Validation:** - Move validation is type-safe - Controller methods have proper return types - Event handling is strongly typed ### Phase 4: Views & Integration (Days 11-14) **Priority: UI Layer & Final Integration** 8. **Migrate Views** - `src/views/BoardRenderer.ts` ✓ (DOM manipulation with types) 9. **Migrate Entry Point** - `src/main.ts` ✓ (application bootstrap) 10. **Update Build Pipeline** - Configure bundler (Rollup/Webpack) for TypeScript - Update HTML to reference compiled JS - Configure source maps - Setup production build 11. **Testing & Documentation** - Migrate Jest tests to TypeScript - Update test configuration - Generate API documentation - Update README with TypeScript info **Validation:** - Application runs identically to JS version - All tests pass - No runtime type errors - Build produces optimized bundle --- ## 4. Build Pipeline Design ### 4.1 Package.json Scripts ```json { "scripts": { "dev": "npm run build:dev && npm run serve", "build": "npm run clean && npm run build:prod", "build:dev": "tsc --project tsconfig.json", "build:prod": "tsc --project tsconfig.build.json", "build:watch": "tsc --project tsconfig.json --watch", "clean": "rimraf dist", "serve": "http-server -p 8080 -o", "type-check": "tsc --noEmit", "test": "jest", "test:watch": "jest --watch", "test:coverage": "jest --coverage", "lint": "eslint src/**/*.ts", "lint:fix": "eslint src/**/*.ts --fix", "format": "prettier --write \"src/**/*.ts\"", "validate": "npm run type-check && npm run lint && npm run test" } } ``` ### 4.2 Dependencies **File: Update to `package.json`** ```json { "devDependencies": { "@types/jest": "^29.5.11", "@types/node": "^20.10.6", "@typescript-eslint/eslint-plugin": "^6.18.0", "@typescript-eslint/parser": "^6.18.0", "eslint": "^8.56.0", "jest": "^29.7.0", "prettier": "^3.1.1", "rimraf": "^5.0.5", "ts-jest": "^29.1.1", "ts-node": "^10.9.2", "typescript": "^5.3.3", "http-server": "^14.1.1" } } ``` ### 4.3 Jest Configuration for TypeScript **File: `jest.config.ts`** ```typescript import type { Config } from 'jest'; const config: Config = { preset: 'ts-jest', testEnvironment: 'jsdom', roots: ['/src', '/tests'], testMatch: [ '**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts' ], transform: { '^.+\\.ts$': ['ts-jest', { tsconfig: 'tsconfig.json', isolatedModules: true }] }, moduleNameMapper: { '^@/(.*)$': '/src/$1', '^@types/(.*)$': '/src/types/$1', '^@pieces/(.*)$': '/src/pieces/$1', '^@game/(.*)$': '/src/game/$1', '^@engine/(.*)$': '/src/engine/$1', '^@controllers/(.*)$': '/src/controllers/$1', '^@views/(.*)$': '/src/views/$1', '^@utils/(.*)$': '/src/utils/$1' }, collectCoverageFrom: [ 'src/**/*.ts', '!src/**/*.d.ts', '!src/types/**', '!src/main.ts' ], coverageThreshold: { global: { branches: 80, functions: 80, lines: 80, statements: 80 } }, setupFilesAfterEnv: ['/tests/setup.ts'] }; export default config; ``` ### 4.4 ESLint Configuration for TypeScript **File: `.eslintrc.json`** ```json { "parser": "@typescript-eslint/parser", "parserOptions": { "ecmaVersion": 2020, "sourceType": "module", "project": "./tsconfig.json" }, "extends": [ "eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended-requiring-type-checking" ], "plugins": ["@typescript-eslint"], "env": { "browser": true, "es2020": true, "node": true }, "rules": { "@typescript-eslint/explicit-function-return-type": "warn", "@typescript-eslint/no-explicit-any": "error", "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], "@typescript-eslint/strict-boolean-expressions": "error", "@typescript-eslint/no-floating-promises": "error", "@typescript-eslint/no-misused-promises": "error" } } ``` --- ## 5. Dual JavaScript/TypeScript Strategy During incremental migration, both JavaScript and TypeScript files will coexist: ### 5.1 Approach 1. **Type Declarations for JS Files** - Create `.d.ts` declaration files for unconverted JS modules - Allows TypeScript files to import JS modules with types 2. **Module Resolution** - TypeScript imports from `src/` (TS files) - Legacy imports from `js/` (JS files) - Use `paths` in `tsconfig.json` to alias imports 3. **Build Process** - Development: Compile only TS files to `dist/` - Keep JS files in `js/` directory - HTML loads from `dist/` or `js/` based on migration status 4. **Testing Strategy** - Migrate tests incrementally with code - Keep existing JS tests running - New TS tests use `ts-jest` ### 5.2 Example: Type Declaration for JS Module **File: `src/types/legacy/Board.d.ts`** (temporary during migration) ```typescript import { Position } from '@types'; import { IPiece } from '@types'; export class Board { grid: Array>; constructor(); setupInitialPosition(): void; getPiece(row: number, col: number): IPiece | null; setPiece(row: number, col: number, piece: IPiece | null): void; movePiece(fromRow: number, fromCol: number, toRow: number, toCol: number): { captured: IPiece | null }; isInBounds(row: number, col: number): boolean; clone(): Board; clear(): void; toFEN(): string; findKing(color: 'white' | 'black'): Position; getPiecesByColor(color: 'white' | 'black'): IPiece[]; getAllPieces(color?: 'white' | 'black' | null): IPiece[]; } ``` ### 5.3 Migration Validation Checklist After each file migration: - [ ] TypeScript file compiles without errors - [ ] No implicit `any` types - [ ] All function signatures have return types - [ ] Tests pass for migrated module - [ ] No breaking changes to public API - [ ] Documentation updated - [ ] Legacy `.d.ts` file removed (if created) --- ## 6. Testing Strategy for TypeScript Code ### 6.1 Test Migration Approach 1. **Keep Existing Tests Working** - Existing Jest tests continue to run against JS files - Incremental conversion to TypeScript 2. **Type-Safe Test Writing** - Use TypeScript for new tests - Strong typing for test data and mocks - Type-safe expect assertions 3. **Test File Structure** ```typescript // Example: tests/unit/pieces/Pawn.test.ts import { Pawn } from '@pieces/Pawn'; import { Board } from '@game/Board'; import { Color, Position } from '@types'; describe('Pawn', () => { let board: Board; let whitePawn: Pawn; let blackPawn: Pawn; beforeEach(() => { board = new Board(); whitePawn = new Pawn(Color.WHITE, { row: 6, col: 4 }); blackPawn = new Pawn(Color.BLACK, { row: 1, col: 4 }); }); describe('getValidMoves', () => { it('should allow white pawn to move forward one square', () => { board.setPiece(6, 4, whitePawn); const moves: Position[] = whitePawn.getValidMoves(board); expect(moves).toContainEqual({ row: 5, col: 4 }); }); it('should allow white pawn to move forward two squares on first move', () => { board.setPiece(6, 4, whitePawn); const moves: Position[] = whitePawn.getValidMoves(board); expect(moves).toContainEqual({ row: 4, col: 4 }); expect(whitePawn.hasMoved).toBe(false); }); }); }); ``` ### 6.2 Type-Safe Mocking ```typescript // Example: Mocking Board for isolated piece tests function createMockBoard(overrides?: Partial): jest.Mocked { return { grid: Array(8).fill(null).map(() => Array(8).fill(null)), getPiece: jest.fn(() => null), setPiece: jest.fn(), isInBounds: jest.fn((row: number, col: number) => row >= 0 && row < 8 && col >= 0 && col < 8 ), clone: jest.fn(), ...overrides } as jest.Mocked; } ``` ### 6.3 Coverage Goals - **Unit Tests**: 90%+ coverage for core logic - **Integration Tests**: Key workflows (move execution, check detection) - **Type Coverage**: 100% (no implicit `any`) --- ## 7. Strict Mode vs. Gradual Typing ### 7.1 Decision: **Strict Mode from Start** **Rationale:** - Codebase is small (~3,700 lines) - manageable for strict migration - Strong typing prevents entire classes of bugs - Easier to maintain type safety from beginning than retrofit later - Modern TypeScript best practice ### 7.2 Strict Mode Configuration All strict checks enabled in `tsconfig.json`: ```json { "strict": true, "noImplicitAny": true, "strictNullChecks": true, "strictFunctionTypes": true, "strictBindCallApply": true, "strictPropertyInitialization": true, "noImplicitThis": true, "alwaysStrict": true } ``` ### 7.3 Handling Strict Challenges **Problem: Strict Null Checks** ```typescript // ❌ JavaScript style function getPiece(row, col) { return this.grid[row][col]; // Could be null } // ✅ TypeScript strict style function getPiece(row: number, col: number): IPiece | null { if (!this.isInBounds(row, col)) { throw new Error(`Position (${row}, ${col}) is out of bounds`); } return this.grid[row][col] ?? null; } ``` **Problem: Strict Property Initialization** ```typescript // ❌ Would fail strict check class GameController { board: Board; // Error: Property has no initializer constructor() { this.setup(); // Initializes in method } } // ✅ Options to fix class GameController { // Option 1: Initialize inline board: Board = new Board(); // Option 2: Use definite assignment assertion (use sparingly) board!: Board; // Option 3: Make nullable and check before use board: Board | null = null; constructor() { this.board = new Board(); } } ``` ### 7.4 Gradual Adoption Strategy (If needed) If strict mode proves too challenging: ```json { "compilerOptions": { "strict": false, "noImplicitAny": true, // Enable immediately "strictNullChecks": false, // Enable in Phase 3 "strictFunctionTypes": true, // Enable immediately "strictBindCallApply": true, // Enable immediately "strictPropertyInitialization": false // Enable in Phase 4 } } ``` --- ## 8. Risk Mitigation Strategies ### 8.1 Technical Risks | Risk | Impact | Probability | Mitigation | |------|--------|-------------|------------| | **Type errors cascade through codebase** | High | Medium | Bottom-up migration; comprehensive type definitions first | | **Circular dependencies emerge** | Medium | Low | Use interfaces; dependency injection patterns | | **Build time increases significantly** | Low | Medium | Use incremental builds; `tsc --watch`; proper `tsconfig` | | **Tests break during migration** | High | Medium | Migrate tests with code; maintain JS test runner during transition | | **Runtime errors from type assumptions** | High | Low | Strict runtime validation; no `as` type assertions without validation | | **DOM type mismatches** | Medium | Medium | Use proper DOM types; add runtime checks | ### 8.2 Process Risks | Risk | Impact | Probability | Mitigation | |------|--------|-------------|------------| | **Scope creep (refactoring beyond typing)** | Medium | High | Clear boundaries: type addition only, not logic changes | | **Incomplete migration stalls** | High | Low | Phased approach; each phase delivers value; dual system works | | **Team unfamiliarity with TypeScript** | Medium | Medium | Documentation; pair programming; iterative review | | **Type definition maintenance burden** | Low | Medium | Use inference where possible; automated type generation tools | ### 8.3 Testing & Validation Risks | Risk | Impact | Probability | Mitigation | |------|--------|-------------|------------| | **Loss of test coverage** | High | Low | Require test migration with code; coverage tracking | | **Integration issues not caught** | High | Medium | E2E tests; manual testing of critical paths each phase | | **Type errors mask runtime bugs** | Medium | Low | Strict mode; runtime validation; comprehensive testing | --- ## 9. Architecture Decision Records (ADRs) ### ADR-001: Strict TypeScript Mode from Start **Status:** Accepted **Date:** 2025-11-23 **Context:** Need to decide between gradual typing (loose config) vs strict typing from the beginning. **Decision:** Enable all strict TypeScript compiler options from the start of migration. **Rationale:** 1. Codebase size is manageable (~3,700 lines) 2. Strict typing prevents entire categories of runtime errors 3. Retrofitting strict types later is significantly harder 4. Team capacity exists to handle strict migration 5. Modern best practice for new TypeScript projects **Consequences:** - Positive: Maximum type safety, fewer runtime errors, better IDE support - Negative: Slower initial migration, more upfront type work - Neutral: May require more type annotations and null checks **Alternatives Considered:** - Gradual typing with `strict: false` - rejected due to lower safety - Phased strict enablement - considered as backup plan if strict proves too difficult --- ### ADR-002: Bottom-Up Migration Order **Status:** Accepted **Date:** 2025-11-23 **Context:** Need to determine order of file migration to minimize circular dependencies and type errors. **Decision:** Migrate in dependency order: utilities → models → engine → controllers → views → main. **Rationale:** 1. Lower-level modules have fewer dependencies 2. Type definitions flow upward naturally 3. Each layer can be fully typed before moving to next 4. Reduces circular dependency issues 5. Allows incremental testing and validation **Consequences:** - Positive: Clean type propagation, easier debugging - Negative: UI changes come last (less visible progress initially) - Neutral: Requires maintaining type declaration files for JS modules temporarily **Alternatives Considered:** - Top-down (main → controllers → models) - rejected due to dependency complexity - Feature-based (all files for one feature) - rejected due to cross-cutting concerns --- ### ADR-003: Path Aliases for Module Resolution **Status:** Accepted **Date:** 2025-11-23 **Context:** Need clean import syntax and avoid relative path hell (`../../../utils/Constants`). **Decision:** Use TypeScript path aliases (`@types`, `@game`, `@pieces`, etc.). **Rationale:** 1. Cleaner, more maintainable imports 2. Easier refactoring (move files without updating imports) 3. Clear module boundaries 4. Common practice in TypeScript projects 5. Better IDE autocomplete **Consequences:** - Positive: Clean imports, easier refactoring - Negative: Requires configuration in both `tsconfig.json` and `jest.config.ts` - Neutral: Build tools need to resolve aliases (most do automatically) **Example:** ```typescript // ❌ Without aliases import { Board } from '../../game/Board'; import { Piece } from '../Piece'; // ✅ With aliases import { Board } from '@game/Board'; import { Piece } from '@pieces/Piece'; ``` --- ### ADR-004: Enum vs String Literal Union Types **Status:** Accepted **Date:** 2025-11-23 **Context:** Need to represent fixed sets of values (piece types, colors, game status, etc.). **Decision:** Use **enums** for semantic types (PieceType, GameStatus, SpecialMove) and **string literal unions** for simple values (Color, File, Rank). **Rationale:** **Use Enums When:** - Type represents a closed set with semantic meaning - Need reverse mapping (value to name) - Values used in multiple contexts - Example: `PieceType.QUEEN`, `GameStatus.CHECKMATE` **Use String Literal Unions When:** - Simple value types with no behavior - Direct string comparisons - JSON serialization preferred - Example: `type Color = 'white' | 'black'` **Consequences:** - Positive: Clear type intent, better type safety - Negative: Mix of patterns (enum vs union) - need documentation - Neutral: Slightly larger bundle size for enums **Examples:** ```typescript // ✅ Enum (semantic type with behavior) export enum PieceType { PAWN = 'pawn', QUEEN = 'queen', // ... } // ✅ String literal union (simple value) export type Color = 'white' | 'black'; export type File = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h'; ``` --- ### ADR-005: Interface vs Type Alias **Status:** Accepted **Date:** 2025-11-23 **Context:** TypeScript offers both `interface` and `type` for defining shapes. **Decision:** Use **interfaces** for object shapes and class contracts. Use **type aliases** for unions, tuples, and complex types. **Rules:** 1. `interface` for object shapes: `interface Position { row: number; col: number; }` 2. `interface` with `I` prefix for contracts: `interface IPiece { ... }` 3. `type` for unions: `type Color = 'white' | 'black'` 4. `type` for complex types: `type MoveHandler = (move: Move) => void` **Rationale:** - Interfaces are extendable and compose better for OOP patterns - Type aliases required for unions and intersections - Consistency with common TypeScript conventions - Clear separation of concerns **Consequences:** - Positive: Clear conventions, better composability - Negative: Need to remember which to use - Neutral: Both approaches are equivalent in many cases --- ## 10. Success Criteria & Validation ### 10.1 Technical Success Metrics - [ ] **Zero TypeScript Errors**: `tsc --noEmit` produces no errors - [ ] **Zero ESLint Errors**: `npm run lint` passes - [ ] **100% Test Pass Rate**: All existing and new tests pass - [ ] **80%+ Code Coverage**: Maintained or improved from JS version - [ ] **Zero `any` Types**: All types explicitly defined (verified via ESLint rule) - [ ] **Build Success**: Production build completes without warnings - [ ] **Bundle Size**: Similar or smaller than JS version (gzip) - [ ] **Runtime Performance**: No measurable degradation ### 10.2 Functional Success Metrics - [ ] **Feature Parity**: All JS functionality works identically - [ ] **UI Unchanged**: Visual appearance and UX identical to JS version - [ ] **No Runtime Errors**: No console errors during normal gameplay - [ ] **Move Validation**: All chess rules enforced correctly - [ ] **State Management**: Game state persists and restores correctly - [ ] **Event System**: All events fire and handle correctly ### 10.3 Developer Experience Metrics - [ ] **IDE Autocomplete**: Full IntelliSense in VS Code/editors - [ ] **Type-Safe Refactoring**: Rename/move operations preserve correctness - [ ] **Clear Error Messages**: TypeScript errors are actionable - [ ] **Fast Builds**: Incremental builds complete in <5 seconds - [ ] **Documentation**: TSDoc comments generate API docs ### 10.4 Final Validation Checklist **Phase 1 Complete:** - [ ] TypeScript build system working - [ ] All type definitions created and exported - [ ] Utilities migrated and tested - [ ] Zero compilation errors **Phase 2 Complete:** - [ ] All piece classes migrated - [ ] Board and GameState migrated - [ ] Core models have 90%+ test coverage - [ ] No implicit `any` types in core **Phase 3 Complete:** - [ ] Chess engine migrated - [ ] Controllers migrated - [ ] All business logic type-safe - [ ] Integration tests pass **Phase 4 Complete:** - [ ] Views migrated - [ ] Main entry point migrated - [ ] Production build working - [ ] All tests migrated to TypeScript - [ ] Documentation updated - [ ] Legacy JS files removed --- ## 11. Next Steps & Action Items ### Immediate Actions (Week 1) 1. **Setup TypeScript Environment** - [ ] Install TypeScript and dependencies - [ ] Create `tsconfig.json` and `tsconfig.build.json` - [ ] Update `package.json` scripts - [ ] Configure ESLint for TypeScript 2. **Create Type Definitions** - [ ] Create `src/types/` directory - [ ] Write `core.types.ts` - [ ] Write `piece.types.ts` - [ ] Write `game.types.ts` - [ ] Write `move.types.ts` - [ ] Write `ui.types.ts` - [ ] Create barrel export `index.ts` 3. **Migrate Utilities** - [ ] Migrate `Constants.ts` - [ ] Migrate `Helpers.ts` - [ ] Migrate `EventBus.ts` - [ ] Write tests for utilities ### Medium-Term Actions (Weeks 2-3) 4. **Migrate Core Models** - [ ] Migrate all piece classes - [ ] Migrate Board and GameState - [ ] Update tests 5. **Migrate Engine & Controllers** - [ ] Migrate MoveValidator and SpecialMoves - [ ] Migrate GameController and DragDropHandler - [ ] Update tests 6. **Migrate Views & Main** - [ ] Migrate BoardRenderer - [ ] Migrate main.ts - [ ] Test end-to-end ### Long-Term Actions (Week 4+) 7. **Finalize & Polish** - [ ] Remove all legacy JS files - [ ] Generate API documentation - [ ] Update README and docs - [ ] Performance testing - [ ] Deploy to production --- ## 12. References & Resources ### TypeScript Documentation - [Official TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/intro.html) - [TypeScript Deep Dive](https://basarat.gitbook.io/typescript/) - [Strict Mode Guide](https://www.typescriptlang.org/tsconfig#strict) ### Migration Guides - [Migrating from JavaScript](https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html) - [React + TypeScript Cheatsheet](https://react-typescript-cheatsheet.netlify.app/) (useful patterns) ### Testing - [Jest with TypeScript](https://jestjs.io/docs/getting-started#using-typescript) - [ts-jest Documentation](https://kulshekhar.github.io/ts-jest/) ### Tools - [TypeScript ESLint](https://typescript-eslint.io/) - [TypeScript Playground](https://www.typescriptlang.org/play) (for quick type checks) ### Internal References - `README.md` - Project overview - `docs/issue-*.md` - Previous issue analyses - `tests/` - Existing test suite --- ## Appendix A: Example Conversions ### A.1 JavaScript → TypeScript: Piece Class **Before (JavaScript):** ```javascript // js/pieces/Pawn.js export class Pawn extends Piece { constructor(color, position) { super(color, position); this.type = 'pawn'; this.value = 100; } getValidMoves(board) { const moves = []; const direction = this.color === 'white' ? -1 : 1; // ... implementation return moves; } } ``` **After (TypeScript):** ```typescript // src/pieces/Pawn.ts import { Piece } from './Piece'; import { Position, Color, PieceType, PieceValue } from '@types'; import { IBoard } from '@types'; export class Pawn extends Piece { constructor(color: Color, position: Position) { super(color, position); this.type = PieceType.PAWN; this.value = 100 as PieceValue; } public getValidMoves(board: IBoard): Position[] { const moves: Position[] = []; const direction: -1 | 1 = this.color === Color.WHITE ? -1 : 1; // ... implementation with type safety return moves; } } ``` ### A.2 JavaScript → TypeScript: Game State **Before (JavaScript):** ```javascript // js/game/GameState.js export class GameState { constructor() { this.moveHistory = []; this.status = 'active'; } recordMove(move) { this.moveHistory.push(move); } } ``` **After (TypeScript):** ```typescript // src/game/GameState.ts import { Move, GameStatus, IGameState } from '@types'; export class GameState implements IGameState { public readonly moveHistory: Move[] = []; public status: GameStatus = GameStatus.ACTIVE; public recordMove(move: Move): void { this.moveHistory.push(move); } } ``` --- ## Appendix B: Troubleshooting Guide ### Common TypeScript Errors **Error:** `Property 'grid' has no initializer` ```typescript // ❌ Problem class Board { grid: BoardGrid; // Error! } // ✅ Solution class Board { grid: BoardGrid = this.initializeGrid(); } ``` **Error:** `Type 'null' is not assignable to type 'IPiece'` ```typescript // ❌ Problem function getPiece(row: number, col: number): IPiece { return this.grid[row][col]; // Error! Could be null } // ✅ Solution function getPiece(row: number, col: number): IPiece | null { return this.grid[row][col] ?? null; } ``` **Error:** `Cannot find module '@types'` ```typescript // ❌ Problem: Path alias not configured import { Position } from '@types'; // ✅ Solution: Check tsconfig.json paths and restart TS server { "compilerOptions": { "paths": { "@types/*": ["src/types/*"] } } } ``` --- ## Document Revision History | Version | Date | Author | Changes | |---------|------|--------|---------| | 1.0 | 2025-11-23 | System Architecture Designer | Initial architecture document | --- **End of Document**