なぜこのエラーが発生するのか
TypeScriptは本質的にあなたのセーフティネットです。この特定のエラーは、変数が使用されるまでに値が確実に保持されているか、コンパイラが100%確信できない場合に発生します。これは strictNullChecks のコア機能であり、undefined によるバグがユーザーに届く前に防ぐように設計されています。
let discount: number;
const isFlashSale = Math.random() > 0.5;
if (isFlashSale) {
discount = 20;
}
// エラー: Variable 'discount' is used before being assigned.
console.log(discount);
上記の例では、isFlashSale が false の場合、discount 変数は空のままになります。TypeScriptはこのリスクを検知し、実行時のクラッシュを防ぐためにビルドをブロックします。
主な原因
この挙動は、TypeScriptの「制御フロー分析(Control Flow Analysis)」に起因します。主に以下のシナリオで発生しやすくなります:
- 不完全な条件分岐:
ifブロックで値を代入したが、elseブロックを忘れている。 - Try-Catch ブロック:
tryブロック内で変数が代入されているが、代入が行われる前にエラーが発生してcatchブロックへ飛ぶ可能性があるとコンパイラが判断している。 - 初期化の遅延: 後で実行される可能性のあるコールバックやネストされた関数内で変数が代入されている。
解決方法
1. デフォルト値を指定する
最も信頼できる修正方法は、変数をすぐに初期化することです。これにより、ロジックが更新をトリガーしなかった場合でも、変数は常に有効なフォールバック値を保持できます。
// 'let role: string;' とする代わりに、デフォルト値を使用します
let role: string = "guest";
if (user.isAdmin) {
role = "admin";
}
console.log(role); // 安全でクリーン
2. 確定的代入言明(!)を使用する
コンパイラよりも開発者の方が状況を把握している場合があります。フレームワークのフックやセットアップ関数などを通じて変数が確実に代入されることが分かっている場合は、宣言時に変数名の後ろに ! を付けます。これにより、TypeScriptに自分の判断を信頼するように伝えます。
let databaseUrl!: string;
function setup() {
databaseUrl = "mongodb://localhost:27017";
}
setup();
console.log(databaseUrl.length); // エラーが抑制されます
プロのヒント: これは控えめに使用してください。もし判断が間違っていた場合、実行時に TypeError が発生し、TypeScriptを使用する本来の目的が損なわれてしまいます。
3. ロジックのループを閉じる
if 文を使用する場合は、考えられるすべてのパスで値が代入されるようにしてください。多くの場合、else ブロックを追加するだけでコンパイラの要件を満たすことができます。
let status: string;
if (response.code === 200) {
status = "Success";
} else {
status = "Error"; // すべてのパスがカバーされました
}
console.log(status);
4. 三項演算子でリファクタリングする
ロジックが単純な場合は、変数を宣言してから後で代入するのではなく、三項演算子を使用して宣言と代入を一度に行います。これにより、メモリ管理や可読性の面で優れたプラクティスである const を使用できるようになります。
// よりクリーンで、エラーを完全に防ぐことができます
const status = response.code === 200 ? "Success" : "Error";
5. Try-Catch シナリオを処理する
APIを扱う際、TypeScriptは try ブロックが失敗する可能性があると想定します。これを解決するには、catch ブロックでも変数が代入されるようにするか、try が始まる前に初期化しておきます。
let userData: User;
try {
userData = await fetchUser();
} catch (err) {
userData = { id: 0, name: "Anonymous" }; // フォールバック代入
}
console.log(userData.name);
修正の確認方法
IDEの「問題(Problems)」タブを確認するか、ターミナルから手動でコンパイラを実行します:
npx tsc --noEmit
ターミナルにエラーが表示されなければ、代入チェックを無事にパスしたことになります。また、新しく設定したデフォルト値によってアプリの期待される動作が変わっていないか、テストスイートを実行して確認することもお勧めします。
ベストプラクティス
- 基本は const を使用する: 変数の90%で
constを使用することを目指しましょう。constが使えない場合は、ロジックを簡素化できるサインであることが多いです。 - strict モードを無効にしない: エラーを隠すために
tsconfig.jsonでstrictNullChecksをオフにしたくなるかもしれませんが、やめておきましょう。それはコンパイル時の警告を、深夜の本番環境でのクラッシュに引き換えているだけです。 - シンプルに保つ: 変数の代入ロジックが50行にわたる場合、コンパイラは解析に苦労します。複雑なロジックは、小さく専用の関数に分割しましょう。

