← 一覧に戻る

TypeScript 移行: TDD(テスト駆動)で安全にリファクタリング

2026年3月9日 22:37 更新
MD から自動変換されたページです。内容について質問があれば右下の ? ボタンからどうぞ。

Context

docs/app.html に1200行のJavaScriptがインラインで埋まっている。 TypeScriptに移行するが、TDD方式で進める:まずテストで「現在の正しい動作」を記録し、その後TypeScriptに書き換え、テストが通ることで動作の同一性を保証する。

現状: HTML + CSS + JS が1ファイル(1940行)に同居。varベース、IIFE ラッパー。 ゴール: テストで守りながら src/app.ts に抽出し、esbuild で docs/app.js にビルド。

アーキテクチャ(移行後)

reinfolib-service/
├── package.json          # 新規: esbuild + typescript + vitest
├── tsconfig.json         # 新規: DOM + ESNext(workers/ とは別)
├── vitest.config.ts      # 新規: vitest 設定
├── src/
│   ├── app.ts            # メインロジック(~1200行)
│   ├── types.ts          # API レスポンス型・内部データ型
│   └── __tests__/
│       ├── stats.test.ts       # 統計計算のテスト
│       ├── zoning.test.ts      # 用途地域ヘルパーのテスト
│       ├── address.test.ts     # 住所解析のテスト
│       ├── building.test.ts    # 建物残価推定のテスト
│       └── format.test.ts      # 表示フォーマットのテスト
├── docs/
│   ├── app.html          # CSS + HTML のみ
│   ├── app.js            # esbuild 出力(コミット対象)
│   ├── index.html        # LP(変更なし)
│   └── stations.json     # 駅データ(変更なし)
└── workers/              # 変更なし

設計判断

判断 結論 理由
テストFW vitest CLAUDE.md 指定。esbuild 互換。TS ネイティブ対応
ビルドツール esbuild 設定ファイル不要。1コマンドで完結
ファイル分割 2ファイル(app.ts + types.ts) KISS優先。テストは別ディレクトリ
モノレポ設定 独立した root package.json workers/ の tsconfig は DOM 型なし。共有不可
TS 厳格度 strict: true DOM要素は as HTMLInputElement 等で型アサーション
ビルド出力 docs/app.js をコミット GitHub Pages は CI なし
CSP unsafe-inline → self 外部JSファイル化でCSP強化

TDD 実装ステップ

Step 1: ビルド&テスト環境セットアップ

新規作成:

{
  "scripts": {
    "build": "esbuild src/app.ts --bundle --outfile=docs/app.js --target=es2017",
    "typecheck": "tsc --noEmit",
    "test": "vitest run",
    "test:watch": "vitest",
    "dev": "esbuild src/app.ts --bundle --outfile=docs/app.js --target=es2017 --watch"
  }
}

Step 2: RED — テストを先に書く(現在のJS関数の動作を記録)

t-wada の「仕様化テスト」アプローチ リファクタリング前にテストを書く手法を「仕様化テスト(Characterization Test)」という。 t-wada は「テストがないコードはレガシーコード」と言う。移行前にテストで現在の動作を"写真に撮る"ことで、移行後に何が壊れたかを即座に検出できる。純粋関数(=ブラウザの画面操作に依存しない関数)に絞るのは「テストの信頼性と速度を最大化する」ため。document.getElementByIdaddEventListener を使う関数はブラウザ環境がないと動かないのでテストが不安定になる。一方、「数値を渡したら数値が返る」ような関数は環境に左右されず、高速・確実にテストできる。

テスト対象は DOM非依存の純粋関数(=ブラウザの画面操作を使わない、入力→出力だけで完結する関数)に絞る。DOM操作(画面の要素を取得・変更する処理)はブラウザ手動確認。 既存 docs/app.html のコードから関数を export 可能な形で抽出しつつ、テストを書く。

テストファイルと対象関数

stats.test.ts — 統計計算(最も重要。坪単価の正確性に直結)

zoning.test.ts — 用途地域ヘルパー

address.test.ts — 住所解析

building.test.ts — 建物残価推定

format.test.ts — 表示フォーマット

Step 3: src/types.ts を作成

APIレスポンスの型定義。テストのテストデータにも使用。

Step 4: GREEN — src/app.ts に移植してテストを通す

  1. IIFE からロジック関数を抽出し export する
  2. varconst / let
  3. 型アノテーション追加
  4. テストが全て通ることを確認(npm test

鉄則: テストが失敗しても、テストを書き換えてはいけない テストは Step 2 で「現在の正しい動作」を記録したもの。失敗したら、それは実装(src/app.ts)のバグ。テストを直すのではなく、実装を直してテストを通す。テストを書き換えると「何が正しい動作か」の基準が失われ、TDD の意味がなくなる。

ポイント: DOM操作部分(イベントハンドラ、レンダリング)は export しない。 テスト可能な「計算ロジック」と「UI操作」を分離する。これが移行の最大の価値。

Step 5: REFACTOR — モダン構文に整理

テストが通った状態で安全にリファクタ:

Step 6: docs/app.html を更新

Step 7: 最終検証

npm run typecheck  # 型エラー 0
npm test           # 全テスト PASS
npm run build      # docs/app.js 生成
# ブラウザで手動確認

テスト設計方針

変更対象ファイル

ファイル 操作 内容
package.json 新規 esbuild + typescript + vitest
tsconfig.json 新規 フロントエンド用 TS 設定
vitest.config.ts 新規 vitest 設定(happy-dom)
src/types.ts 新規 型定義(~80行)
src/app.ts 新規 メインロジック(~1200行、関数を export)
src/__tests__/*.test.ts 新規 テスト5ファイル
docs/app.html 編集 script タグ→外部参照、CSP 更新
docs/app.js 生成 esbuild 出力(コミット対象)

検証方法

  1. npm test — 全テスト PASS
  2. npm run typecheck — 型エラー 0
  3. npm run build — esbuild 正常終了
  4. ブラウザで docs/app.html を開き、住所検索・駅名検索が動作すること
  5. 既存 Python E2E テスト(pytest tests/test_e2e.py)も引き続き PASS
📝 質問モード — テキストを選択してね
✓ 質問を送信しました