型だけを直す変更は、ビルド成果物が1バイトも変わらないことで証明する

型だけを直す変更は、ビルド成果物が1バイトも変わらないことで証明する

おもちかえり.com(Vue + Firebase)で、型の穴をまとめてふさぐ作業をしました。Firestore の data() を DocumentData のまま流している所、as で言い切っている所、type: Object のままの props などです。条件は一つだけで、「実行するコードは変えないで、型だけ直す」。

問題は、それをどう確かめるかです。テストが通っても、テストが見ていない所の動きが変わっていないとは言えません。レビューで「型しか触っていません」と書いても、それは書いた人の主張です。

型は実行時に消える。だから成果物を比べればよい

TypeScript の型注釈、as、generics、satisfies、import type は、どれもビルドで消えます。つまり本当に型しか触っていないなら、ビルド成果物は1バイトも変わらないはずです。逆に言えば、成果物が同じなら、実行時の動きが変わらないことはテストより強く言えます。

やることは単純で、変更前と変更後で同じようにビルドして比べるだけです。

# 変更前(main)
git stash
yarn build && cp -r dist /tmp/dist-main
git stash pop

# 変更後
yarn build && cp -r dist /tmp/dist-new

diff -rq /tmp/dist-main /tmp/dist-new && echo "DIST IDENTICAL"

PR の本文には「main とこのブランチの dist を diff -rq で比べて同じ」と一行書きます。レビューする側は、型の一行一行を「本当に実行時に影響しないか」と疑わずに済みます。

要らない as は機械に消させる

as の中には、外しても型が変わらないものが混ざっています。これは typescript-eslint の no-unnecessary-type-assertion が見つけてくれて、--fix で消せます。

npx eslint src --fix \
  --rule '{"@typescript-eslint/no-unnecessary-type-assertion":"error"}'

「外しても型が変わらない」ものしか触らないので、これも成果物は変わりません。

残りの as は、外すと型が広がるものです。ここは一つずつ試すと時間がかかるので、まとめて外し、typecheck が落ちたファイルだけ元に戻すのを繰り返しました。外した行を JSON に記録しておくと、戻すのも機械的にできます。落ちなかったファイルは、代入先が広い型をもともと受けていたということなので、そのまま外せます。

ハッシュの連鎖で「全部違う」に見えるとき

Vite の出力はファイル名に中身のハッシュが付きます。一つのチャンクが変わると、それを import しているチャンクのハッシュも変わり、diff -rq では全部のファイルが違って見えます。

このときは、ファイル名と中身の両方からハッシュを剥がしてから比べます。

H = re.compile(r"-[A-Za-z0-9_-]{8}(?=\.(?:js|css|map)\b)")
# 名前も中身も H.sub("", ...) してから、ファイルごとに比べる

eslint-disable-next-line を消す変更でこれをやったら、違いは ()=>{} が ()=>void 0 になった所だけでした。どちらも undefined を返すだけの関数です。new X() を void new X() にした所は、縮める段階で void が消えて、出力は同じでした。違いが残ったときは、その違いを読めば済む、というところまで持っていけます。

同じにならない変更もある

Firestore の data() as Model を、値をそのまま返す関数に置き換えた所もあります。

// 値には触れず型だけを当てる。当て場所をここ一箇所に集める。
export const collectionData = <T = DocumentData>(data: DocumentData): T =>
  data as T;

これは関数の呼び出しが増えるので、成果物は変わります。この手の変更では「成果物が同じ」とは言えないので、PR に「増えるのは引数をそのまま返す関数の呼び出しだけ」と書き、関係する e2e を回し、別のモデル(Codex)にもレビューさせました。証明できる変更と、説明するしかない変更を PR ごとに分けておくと、レビューの重さを変えられます。

気をつけること

型の直しは、量が多いわりに一つ一つは地味で、レビューが流し読みになりがちです。「成果物が同じ」は、読まずに信じてよい数少ない主張です。

この記事をシェア

関連記事

記事一覧に戻る