六千七百行の単一HTMLをやめた。TypeScriptとViteに移すまでにやったこと

型が無いことの不安に耐えられなくなった。分割の境界は、迷わず既存のコメントに従った。

日本進化シミュレーション第3回 / 全16回目次

このゲーム、最初は本当に1枚のHTMLファイルだった。
CSSもJavaScriptも全部その中に書いて、ブラウザで開けば動く。
気楽でよかった。少なくとも最初のうちは。

それが6,700行を超えたあたりから、様子がおかしくなってきた。
関数を一つ直したいだけなのに、目的の場所を探すのにスクロールで数秒かかる。
似た名前の変数がどこにあるか、エディタの検索に頼りきりになる。
「動いているものを壊さずに直す」ことへの不安が、日に日に大きくなっていった。

単一ファイルの限界は、行数そのものではなかった

正直に言うと、行数が多いこと自体はそこまで問題じゃなかったと思う。
本当につらかったのは、型がまったく無いことだった。

建物を配置する関数に渡す引数の順番を一つ間違えても、実行するまで気づけない。
「この変数、今どんな形をしてるんだっけ」を確認するために、定義まで戻って読み直す。
その繰り返しが、機能を足すたびの助走を長くしていった。

ここで僕は、TypeScriptとViteに移すことを決めた。
型があれば、引数を間違えた瞬間にエディタが教えてくれる。
ファイルを分ければ、目的の場所へ一直線に飛べる。

分け方は、悩まずに済ませた

ゼロから設計を考え直すと、たぶんそこで一週間くらい溶ける。
だから、元のファイルにもともと入っていた26個のセクション区切りコメントを、そのままファイル境界にした。

「地形」「道路」「建物モデル」「AI」というくくりは、書いていた自分がその時々で自然に付けていたものだ。
機械的に切っただけなのに、できあがった約28個のファイルは、思っていたよりずっと素直な構成になった。
設計のセンスというより、過去の自分が残していた区切りに助けられた形だ。

移してみて分かったこと

実際にTypeScript化を進めると、今まで見えていなかった緩さがいくつも表面化した。

JavaScriptでは、関数の呼び出し側で末尾の引数を省略すると、勝手にundefinedになって何も起きない。
TypeScriptは違う。デフォルト値も?も付いていない引数は、必ず渡すことを要求してくる。
建物を作るbox(w,h,d,color,opt)のような関数を4つの引数だけで呼んでいた箇所が、一気にエラーの山になった。

直し方はいたって単純で、省略してよい引数に?を一つ付けるだけだった。
けれど、この山を前にして「今まで動いていたのは偶然だったのか」と、ちょっと背筋が寒くなった。

デバッグ用のフックだけは、型のことを忘れることにした

ブラウザのコンソールから直接叩けるwindow.__xxxという関数を、検証用にたくさん生やしている。
これは動的に生えるプロパティなので、律儀に型を付けようとすると際限がない。

interface Window { [key: string]: any }と一行書いて、この種類のプロパティだけは型チェックの対象外にした。
全部を厳格にする必要はなくて、緩めていい場所は最初から緩めておく。
そのほうが、本当に守りたい場所の型が際立つ気がしている。

今の構成

いま動いているのはこんな形だ。

  • index.htmlはViteのエントリーだけで、中身はほとんど空
  • src/*.tsが約28ファイル、元の26セクションに沿って分割
  • npm run devでHMR付きの開発サーバー、npm run buildで配布物になるdist/index.htmlを生成

単一ファイルの気楽さは、たしかに手放した。
その代わり、直したい場所へ迷わず行けるようになったし、引数を間違えた瞬間に教えてもらえるようになった。
次の何回かは、この移行の途中で実際に踏んだ落とし穴の話を書いていく。
分割しただけで、ちゃんと事件はいくつも起きた。

スタンプラリーに挑戦する 行った場所・気になる場所は、現地チェックインでスタンプに残せます。 みんなのコースを見てみる 会員が作ったスポット巡りのコースを都道府県から探せます。自分だけのコースも作れます。 運営者の個人noteも書いています AI活用やサイト運営で気づいたことを、もう少し個人の視点で掘り下げています。

※運営者が個人で書いているnoteです。

技術ブログ一覧へ戻る