こんにちは。フロントエンドチームのシニアアーキテクトです。
日々、モダンなWebアプリケーション開発でTypeScriptと格闘している君なら、一度はこんな絶望を味わったことがあるはずだ。「いや、絶対にここに値はあるはずなのに、なんでTypeScriptは`Object is possibly ‘null’`って怒るんだよ!」と。
APIから返ってくるデータ、ローカルストレージの値、あるいはDOMの要素取得。現場のコードは、しばしば「nullやundefinedの地雷原」だ。この地雷を華麗に、かつ安全に踏み抜かずに進むための強力な武器が、TypeScript標準のユーティリティ型 `NonNullable
今回は、この`NonNullable
—
1. `NonNullable` とは何か?(公式仕様の裏側)
まず基本のおさらいだ。`NonNullable
その実装は、TypeScriptの型定義ファイル(`lib.es5.d.ts`など)を覗けば一目瞭然、実はたったこれだけのコードで書かれている。
type NonNullable
「……は?これだけ?」と思ったかい?そう、これだけだ。
ここで行われているのは、TypeScriptの条件付き型(Conditional Types)と分散条件付き型(Distributive Conditional Types)の合わせ技だ。
ユニオン型を `T` に渡すと、TypeScriptは内部で自動的にそれをバラバラに分解し、一つひとつの型に対して「お前、`null`か`undefined`か?」と問いかける。もし該当すれば `never`(「そんな型は存在しない=消滅させる」という意味)に置き換え、該当しなけらばそのまま残す。
この仕組みによって、混沌としたユニオン型から「無駄なノイズ」を完璧に排除できるというわけだ。
—
2. ブラウザとランタイムの現実、そしてTypeScriptの役割
ここで少し立ち止まって、ブラウザの裏側の話をしよう。
JavaScriptのランタイム(V8エンジンなど)にとって、`null` も `undefined` も、突き詰めれば「値が存在しない」ことを示すプリミティブな概念やメモリ上の番地に過ぎない。ブラウザは、実行時に「あ、これnullだわ、プロパティ読み取れないからTypeError吐こ」と、泥臭く動いているだけだ。
TypeScriptの型チェックは、このブラウザのランタイムエラーを、実行する前に静的にねじ伏せるためのものだ。
しかし、実務で厄介なのは「TypeScriptの型定義」と「実際のAPIレスポンスやDOMの状態」が乖離している瞬間だ。例えば、本当は絶対存在するDOM要素を取ろうとしているのに、型定義が `HTMLElement | null` になっているがために、その後の処理で毎回ガードを書かされる。
const header = document.getElementById(‘main-header’);
// headerがnullかもしれないので、毎回 ?. や if文を書く羽目になる
header.style.backgroundColor = ‘red’; // 怒られる
ここで `NonNullable
—
3. 実務で即コピペできる!実践コードパターン
百聞は一見にしかず。現場でよく遭遇するシチュエーションをベースに、`NonNullable
パターンA: フォームの入力値やAPIレスポンスのフィルタリング
例えば、ユーザーのIDリストの配列の中に、オプションとして `null` や `undefined` が混ざってしまっているケースだ。これをそのまま処理するとバグの元になる。
// APIから返ってきた、あるいはフォームから取得した生データ(nullが混ざっている)
type RawUserIds = (string | null | undefined)[];
// NonNullableを使って、各要素からnullとundefinedをパージした型を作る
type CleanUserIds = NonNullable
// 展開すると string[] になる!
function processUserIds(ids: RawUserIds) {
// 実行時(ランタイム)のフィルター
// ここでフィルタリングしても、従来の型推論だと配列の型が string | null のままのことがある
const validIds: CleanUserIds = ids.filter(
(id): id is NonNullable
);
// validIds は完全に string[] として扱えるため、安心して処理できる
validIds.forEach(id => {
console.log(id.toUpperCase()); // string型なので安心してメソッドが呼べる
});
}
ここでポイントなのは、ユーザー定義型ガード(`id is NonNullable
パターンB: 設定オブジェクトのオプショナルプロパティを一括強制する
アプリケーションの初期化時、部分的な設定(Partial)を受け取るが、内部でデフォルト値をマージした後は「すべての設定が必ず揃っている状態」の型に変換したいときがある。
type AppConfig = {
theme: ‘light’ | ‘dark’ | null;
apiEndpoint: string | undefined;
timeout: number | null;
};
// すべてのプロパティから null と undefined を強制的に排除した型を作る
type ValidatedConfig = {
[K in keyof AppConfig]: NonNullable
};
// 結果:
// {
// theme: “light” | “dark”;
// apiEndpoint: string;
// timeout: number;
// }
function initializeApp(config: AppConfig) {
// デフォルト値をパッチして完全なオブジェクトを作る例
const resolvedConfig: ValidatedConfig = {
theme: config.theme ?? ‘light’,
apiEndpoint: config.apiEndpoint ?? ‘https://api.example.com’,
timeout: config.timeout ?? 5000,
};
// resolvedConfig のプロパティは絶対に null/undefined にならない
console.log(resolvedConfig.theme.toUpperCase());
}
このイディオムは、大規模なアプリケーションの初期化ロジックや、複雑なRedux/Zustandのストアの初期化において、バグの温床を断ち切るために非常に有効だ。
—
4. シニアから後輩へ贈るベストプラクティスと注意点
最後に、現場で `NonNullable
1. 「型汚染」の現実逃避に使わない
APIの設計がガバガバで、本来必須なはずのフィールドが平気で `null` で返ってくるような現場はある。その時に、面倒だからといって安易に `NonNullable
2. `undefined` との付き合い方
JavaScriptにおいて、存在しないプロパティアクセスは `undefined` になる。`NonNullable
—
まとめ
`NonNullable
TypeScriptを書いている時間は、型エラーと戦う時間ではなく、「ビジネスロジックをいかに安全に、美しく表現するか」を楽しむ時間であるべきだ。
明日からのコードレビューで、無駄な `null` チェックの山を見つけたら、「ここに `NonNullable` 使えるんじゃないか?」と、ぜひ後輩にドヤ顔でアドバイスしてみてくれ。応援しているぞ!

コメント