この記事はこんな人におすすめ
-
TypeScriptプロジェクトで
tscを実行したら大量のエラーが出た -
既存エラーがある環境で、自分の変更による影響を切り分けたい
-
「コマンドが失敗した = 自分の変更が原因」と考えてしまったことがある
-
AIに実装や検証を任せるとき、結果をどう評価すればよいか考えたい
この記事で得られること
この記事では、既存の型エラーがあるTypeScriptプロジェクトを例に、
-
tscの失敗という「事実」と、その「原因」を分けて考える方法 -
変更前後のbaselineを使って、新しく増えたエラーを切り分ける方法
-
変更ファイルだけを確認する場合の限界
-
検証結果から「何が証明できたのか」を考える視点
を整理します。
今回の学びを一言で表すと、
「エラーがある」と「自分の変更がエラーを発生させた」は別。
です。
はじめに
TypeScriptのコードを変更したあと、型エラーがないことを確認するためにtscを実行しました。
npx tsc --noEmit
ところが、結果は大量のエラー。
最初にこの結果を見ると、
自分の変更で型エラーを発生させてしまった?
と考えたくなります。
しかし、プロジェクト全体を見ると、今回変更していない箇所にも以前から存在している型エラーがありました。
ここで重要だったのは、
「
tscが失敗した」という事実と、「今回の変更が原因でtscが失敗した」という判断は別
だということです。
この記事では、既存の型エラーがあるTypeScriptプロジェクトで、変更によって新しい問題を発生させていないか切り分ける考え方を整理します。
「エラーがある」と「自分がエラーを増やした」は別
例えば、説明を単純化して、変更後の型チェックで100件のエラーが確認されたとします。
変更後:100件
ここから分かる事実は、
現在のコードベースに100件の型エラーがある
ということだけです。
まだ、
今回の変更によって100件のエラーが発生した
とは判断できません。
もしかすると、
変更前:100件
変更後:100件
かもしれません。
あるいは、
変更前:100件
変更後:103件
かもしれません。
この2つでは意味がまったく異なります。
tscが失敗した
↓
今回の変更が原因だ
とすぐに結論づけるのではなく、
tscが失敗した
↓
これは事実
↓
では、このエラーは変更前からあった?
↓
今回の変更によって増えた?
↓
変更との因果関係はある?
と分けて考える必要があります。
まず変更前のbaselineを取る
この切り分けをするうえで最も分かりやすいのが、変更前の状態を残しておくことです。
例えば、変更前に次のコマンドを実行します。
npx tsc --noEmit --pretty false \
> /tmp/tsc-before.txt 2>&1
変更後にも同じ条件で実行します。
npx tsc --noEmit --pretty false \
> /tmp/tsc-after.txt 2>&1
そして差分を確認します。
diff -u /tmp/tsc-before.txt /tmp/tsc-after.txt
なお、比較するときはTypeScriptや依存関係、tsconfig などの実行条件を揃える必要があります。条件そのものが変われば、コード変更以外の理由でも診断結果が変化する可能性があります。
考え方はシンプルです。
変更前の状態
↓
変更
↓
変更後の状態
↓
差分を見る
既存エラーが大量に残っていたとしても、
今回の変更によって何が変わったのか
を切り分けやすくなります。
baselineがあると「成功・失敗」以外で考えられる
正常なプロジェクトであれば、
npx tsc --noEmit
が成功するかどうかを見るだけでも十分な場合があります。
しかし、すでに既存エラーがある場合、
PASS / FAIL
だけでは今回の変更を評価できません。
例えば、
Before
既存エラー:100件
After
既存エラー:100件
なら、プロジェクト全体の型チェックは依然として失敗しています。
しかし、
今回の変更によって新しい型エラーが追加された
とは限りません。
逆に、
Before
既存エラー:100件
After
既存エラー:101件
なら、その1件を確認する必要があります。
ここで見たいのは絶対的な状態だけではなく、
変更によって何が増減したのか
です。
なお、エラー件数だけでは新しいエラーの有無は判断できません。変更前後で件数が同じでも、既存エラーが解消される一方で新しいエラーが追加されている可能性があります。そのため、件数だけでなく診断内容の差分を確認します。
変更ファイルだけを見る方法もある
全体の型チェック結果が非常に大きい場合、補助的に変更したファイルに関するエラーを絞り込むこともできます。
例えば、変更したTypeScriptファイルを取得します。
git diff --name-only --diff-filter=AM -- '*.ts' '*.tsx'
型チェック結果から、それらのファイルに関係する出力だけを検索することもできます。
npx tsc --noEmit --pretty false 2>&1 |
rg -F -f <(
git diff --name-only --diff-filter=AM -- '*.ts' '*.tsx'
)
ただし、ここには注意が必要です。
変更したファイルにエラーが表示されていない = 今回の変更が新しい型エラーを一切発生させていない
とは限りません。
あるファイルの型を変更した結果、その型を利用している別ファイルでエラーが発生することもあるからです。
変更したファイル A
↓
型が変わる
↓
依存しているファイル B
↓
型エラー
この場合、エラーが表示されるのはBです。
そのため、
変更ファイルだけ確認する
のはあくまで補助的な確認。
より重要なのは、
変更前と変更後の型チェック結果そのものを比較する
ことだと考えました。
TypeScriptがメモリ不足になった場合は、型エラーと分けて考える
型チェックをしていると、別の問題に遭遇することもあります。
例えば、
JavaScript heap out of memory
です。
これは、
TypeScriptのコードに型エラーがある
ことを意味しているわけではありません。
型解析を実行しているNode.jsプロセスが、利用可能なヒープの上限に達した状態です。
利用できるメモリ量を考慮したうえで、例えば次のようにヒープ上限を調整して再実行できます。
node --max-old-space-size=8192 \
./node_modules/typescript/bin/tsc \
--noEmit \
--pretty false
ここでも重要なのは、
tscが正常終了しなかった
という現象だけを見て、
型エラーが原因
と決めつけないことです。
tscが完走しない
↓
なぜ?
↓
型エラー?
メモリ不足?
設定?
別の原因?
と分解して確認します。
「変更ファイルにエラーなし」も証拠の一つにすぎない
今回のような状況では、検証結果の表現にも注意が必要だと感じました。
例えば、
型チェックOK
と書いてしまうと、
プロジェクト全体の
tscが正常終了した
ようにも読めます。
実際には、
プロジェクト全体
→ 既存の型エラーが残っている
変更ファイル
→ 直接表示される新しいエラーは確認できなかった
変更前後
→ 新しいエラーが増えていないことを確認した
という状態かもしれません。
ならば、そのまま伝えた方が正確です。
例えば、
プロジェクト全体には既存の型エラーがあります。変更前後の型チェック結果を比較し、今回の変更によって新しい型エラーが追加されていないことを確認しました。
のように、何を確認したのかを具体的に書きます。
これは単なる言葉遣いの問題ではありません。
検証結果を正しく伝えるには、
どの方法で、どの範囲まで確認できたのか
を自分自身が理解している必要があります。
AIが「エラーがあります」と言ったときも同じ
AIを使って実装や検証を進めていると、
型チェックを実行したところ、既存のエラーがありました。
あるいは、
変更したファイルにはエラーがありません。
といった報告を受けることがあります。
ここでも、その文章だけで判断しないようにしたいと思いました。
確認したいのは、
いつ実行した?
↓
変更前にも同じエラーはあった?
↓
変更後に増えたエラーはある?
↓
変更ファイルだけを見た?
↓
プロジェクト全体を見た?
↓
そもそもtscは最後まで実行できた?
という部分です。
AIがコマンドを実行できても、
その結果が何を証明しているのか
を判断する必要は残ります。
今回の学びは「エラーを直す方法」ではなかった
今回の経験で印象に残ったのは、特定のTypeScriptコマンドそのものよりも、問題の見方でした。
最初に見えているのは、
tscが失敗した
という現象です。
そこから、
自分の変更が悪い
とすぐに原因を決めるのではなく、
事実
↓
tscが失敗した
仮説
↓
今回の変更が原因かもしれない
確認
↓
変更前にも存在した?
新しく増えた?
どのファイル?
今回の変更とつながっている?
判断
↓
今回の変更によるエラーなのか
既存の問題なのか
と分けて考える。
これはTypeScriptに限った話ではありません。
テストが失敗したときも、buildが失敗したときも、画面で不具合が起きたときも、
目の前の現象と、その原因だと思っているものは別
です。
まとめ
既存の型エラーがあるプロジェクトでtscを実行すると、
大量のエラー
が表示されることがあります。
しかし、そこから分かるのは、
現在エラーが存在する
という事実までです。
今回の変更が原因なのかを判断するには、
変更前の状態を確認する
↓
変更後の状態を確認する
↓
差分を見る
↓
今回の変更との因果関係を確認する
必要があります。
今回、自分の中に残った判断基準は、
「エラーがある」と「自分の変更がエラーを発生させた」を分ける。
というものです。
そしてAIが実装や検証をしてくれる場合でも、
tscが失敗した
という結果だけではなく、
その結果は、今回の変更について何を証明しているのか?
まで考える。
TypeScriptの型チェックを題材にしましたが、最終的にはデバッグや検証全般に使える考え方です。