【実務・中級編】 tsconfigのstrictPropertyInitializationオプション – TypeScript実践ガイド

なぜその「初期化漏れ」は、あなたのプロダクトを破壊するのか?

やあ。今日はTypeScriptの `tsconfig.json` に潜む、地味だけど極めて重要なオプション、`strictPropertyInitialization` について話そう。

現場でコードレビューをしていると、クラスコンポーネントやサービス層のクラスを定義した際に、プロパティの初期化をコンストラクタで忘れていたり、あるいは「あとで初期化するから」と安易に `!`(非Nullアサーション演算子)で誤魔化しているコードによく出くわす。

「動くからいいじゃん」? いや、甘い。その油断が、数ヶ月後の深夜、原因不明の `TypeError: Cannot read property ‘x’ of undefined` を引き起こす引き金になるんだ。

`strictPropertyInitialization` とは何か?

一言で言えば、「クラスのプロパティはコンストラクタで必ず初期化せよ」というTypeScriptからの強い警告だ。

このオプションを `true` にすると、クラス内で宣言されたプロパティがコンストラクタ内で確実に代入されていない場合、TypeScriptは容赦なくコンパイルエラーを吐く。

// strictPropertyInitialization: true の世界
class UserProfile {
// コンストラクタで初期化されていないため、ここでエラーが出る
// Property ‘name’ has no initializer and is not definitely assigned in the constructor.
name: string;

constructor() {
// ここで name に値を代入していないのが原因
}
}

ブラウザの裏側で起きていること

なぜこれが重要なのか。JavaScriptの実行エンジン(V8など)の視点で考えてみよう。

JSにおいて、未初期化のプロパティは `undefined` として扱われる。もし君が「このクラスのプロパティは絶対に string だ」と信じ込んでコードを書いても、コンストラクタで初期化を忘れていれば、実行時のオブジェクトは `undefined` を保持したままメモリ上に生成される。

後続のロジックでそのプロパティを参照した瞬間、エンジンは「おっと、期待した型じゃないぞ」とは教えてくれない。そのまま `undefined` として処理を進め、どこかでメソッドを呼び出した瞬間に「オブジェクトじゃないものを呼ぶな」とクラッシュする。

TypeScriptの型システムは、「実行時に発生しうる『未定義』という爆弾を、コンパイル時にすべて爆発させて無効化する」ためのものだ。このオプションを無効にするということは、その爆弾の信管をわざわざ外して放置するようなものなんだよ。

現場で戦うためのベストプラクティス

とはいえ、実務では「どうしてもコンストラクタで初期化できない(例:フレームワークのライフサイクルで注入される)」というケースもあるだろう。その時のスマートな対処法を教える。

1. 基本はコンストラクタでやる

これが王道だ。

class ConfigService {
private readonly apiKey: string;

constructor(apiKey: string) {
this.apiKey = apiKey; // 確実に初期化する
}
}

2. 非同期初期化やDIを使う場合

最近のフロントエンド(ReactやAngularなど)では、DI(依存注入)コンテナを使うことが多い。その場合は「代入はしていないが、初期化は保証されている」ことをコンパイラに伝えるために、あえて `!` を使うのが許される唯一のケースだ。

class ApiClient {
// 外部から必ず注入されることを明示的に伝える(! を使用)
private apiService!: ApiService;

public setApiService(service: ApiService) {
this.apiService = service;
}

public fetchData() {
// ここでは確実に apiService が存在すると信頼して呼び出す
return this.apiService.get(‘/data’);
}
}

3. クラスを使わず「関数型」に寄せる

もしクラスの初期化に悩まされているなら、そもそも「クラスを使うべきではない」というサインかもしれない。TypeScriptにおいて、複雑な初期化が必要なState管理は、クラスよりもクロージャや関数を使ったファクトリパターンの方が、型安全性を維持しやすい傾向にある。

最後に:シニアからのアドバイス

`strictPropertyInitialization` をオフにするのは簡単だ。しかし、それは「TypeScriptの恩恵を半分捨てている」のと同じことだ。

もしプロジェクトの設定でこのオプションが `false` になっているなら、今すぐ `true` に変えてみよう。何百ものエラーが出て絶望するかもしれない。でも、そのエラーこそが、今まで隠れていた「潜在的なバグ」だ。一つずつ潰していく過程で、君のコードの堅牢性は劇的に向上するはずだ。

「動くコード」ではなく「壊れないコード」を書く。それが、プロのエンジニアとしての最低限の矜持だ。明日からの開発、少しだけ厳格にいこうじゃないか。

コメント

タイトルとURLをコピーしました