Post

Copilot を使って JavaScript のコードベースを TypeScript に変換した話

Copilot を使って JavaScript のコードベースを TypeScript に変換した話

1 人のエンジニアで作成された JavaScript の Web アプリケーションを引き継ぐことになりました。 動いてはいるのだけど、他の人がメンテナンスしようとすると「あれ、これってどっち?」が多くてつらい。

ということで、TypeScript に変換する話になりました。

手で 1 ファイルずつ直す方法もありますが、量がそれなりにあると普通に心が折れます。 今回の規模感だと、手作業でやると 2 週間くらいはかかりそうでした。結果的には Copilot を使って 3 日で完了しました。しかも 他の作業を並行しながらです。

やり方は次の 4 つです。

  • TypeScript をトランスパイルできる状態にする
  • Copilot トレーニングとプロンプトの作成
  • アプリケーションの変換
  • 動作確認

背景:なぜ TypeScript 化が必要だったか

引き継ぎで一番困るのは「正しさがコードの外にある」状態です。

  • 暗黙の仕様が多い
  • 変更の影響範囲が読みにくい
  • レビューが “見た目” ではなく “推理” になりがち
  • 新しく入った人が安心して触れない

TypeScript 化で全部解決、という話ではないですが、少なくとも 意図が型として残る ので保守の前提が揃いやすくなります。

手でやる vs Copilot を使う

手動変換は確実です。 ただ、確実さの代わりに時間が溶けます。

Copilot は大量の定型変換が速い。 一方で、放っておくとプロジェクトの流儀とズレた変換も平気で混ざるので、今回は Copilot を「賢い変換機」として扱い、手順で品質を担保する方針にしました。

1. TypeScript をトランスパイルできる状態にする

最初にやったのは「変換」ではなく「土台作り」です。

  • TypeScript を入れてビルドが通る導線を作る
  • tsconfig を置く
  • 既存の JavaScript と共存できるようにする(段階的に移行したいので)

ここが整ってない状態で変換を始めると、途中から “何が原因で壊れたか” が分からなくなります。 先に 壊れ方を観測できる状態 を作るのが大事でした。

2. Copilot トレーニングとプロンプトの作成

今回のメインはここです。いきなり全ファイルを変換しません。 まずはアプリから 代表的なファイル をいくつか抽出して、Copilot と一緒に変換していきました。

  • コンポーネント
  • state 周り
  • API 呼び出し
  • util / helper
  • custom Hooks

この段階で見ていたのは「型を付けること」より、変換後のコードの“形” です。 (export の方針、型の置き場所、any の扱い、React の書き方、など)

そして、ここで一番大事にしたルールがあります。

不具合があっても“不具合として”移行する(このタイミングで直さない)

TypeScript 化のついでにバグが直ってしまうと、見た目は良くても後が地獄になります。 「動作が変わった」時に、その原因が

  • マイグレーション(変換)によるものなのか
  • もともとの不具合がたまたま解消された(あるいは悪化した)のか
  • Copilot が気を利かせてロジックを変えたのか

が判別できなくなるからです。

なので Copilot には明示的にこう指示しました。

  • 挙動は変えない
  • リファクタしない
  • バグは修正しない(現状の挙動を維持する)
  • 型を付けるために必要な最小限の変更だけにする

望みの結果になった段階で、Copilot に「次のファイルを変換する時に使うプロンプト」を生成させます。 このときも、上のルール(挙動不変・バグはそのまま)をプロンプトに含めて固定しました。

3. アプリケーションの変換(プロンプト駆動で回す)

2 で作成したプロンプトを使って、あとはひたすらファイルを変換します。

ただし、途中で不具合が見つかったら止めます。

  • どのパターンで壊れたかを特定する
  • 2 のプロセスに戻る
  • プロンプトの中で「変更したい部分」を明確に伝える
  • 新しいプロンプトを生成して、再開する

この “戻ってプロンプトを育てる” を挟むことで、後半に行くほど変換が安定しました。 逆にここをサボると、終盤で同じ種類のバグが量産されて地獄になります。

途中不具合があってもそのまま変換してくれる。

4. 動作確認:Storybook がめちゃくちゃ効いた

変換で一番つらいのは「壊れた場所を見つけること」です。 今回はコンポーネント部分を事前に Storybook でカバレッジ 100% にしておいたので、変換で壊れた部分を見つけるのにかなり役立ちました。

また、PureFunction や custom Hooks などは、事前にテストを追加しておくことで「壊れていない」ことを保証できました。 この手の土台があると、TypeScript 化の難易度が一気に下がります。

残念ながらアプリケーション全体の E2E テストがなかったので、最後は手動テストが必要でした。 E2E があればもっと楽に、もっと安心して進められたと思います。

まとめ:Copilot 移行は「プロンプトを育てる仕事」だった

Copilot を使うと変換スピードは上がります。 ただし、それは「変換」だけの話です。

本当に効いたのは、

  • 先に TypeScript の土台を作る
  • 代表ファイルで変換の型(ルール)を決める
  • 不具合が出たらプロンプトを更新するフィードバックループを回す
  • Storybook / テストで壊れた場所を最短で見つける

この 4 点でした。

体感として、変換作業そのものは「2 週間コース」から「3 日」に短縮できました。しかも他の作業を並行しながらです。 Copilot の賢さというより、プロンプトを育てて、検証導線で壊れた箇所をすぐ見つけられる状態にしておくことの方が最終的に効きます。

This post is licensed under CC BY 4.0 by the author.