こんにちは。チームを支えるシニアフロントエンドエンジニアの私だ。
君も日々の開発で、APIから返ってきたレスポンスや、フォームの入力値の型定義に頭を悩ませていることだろう。「本当はここ、絶対に`null`も`undefined`も入らないはずなのに、TypeScriptの型推論が優しすぎて(あるいは厳しすぎて)`string | null | undefined`になっちまう……!」なんて叫び声をあげた夜が、一度や二度ではないはずだ。
型安全を追求するあまり、コードのあちこちに無駄なオプショナルチェイニングやガード構文(`if (val != null)`)を書き散らして、ロジックが見づらくなっていませんか?
今回は、そんな日々のフラストレーションを鮮やかに解消してくれる、TypeScript標準のユーティリティ型 `NonNullable
—
1. そもそも `NonNullable` とは何か?(基本のおさらい)
公式ドキュメントを開けば、「型 `T` から `null` と `undefined` を除外する」とサラッと書いてある。だが、その本質をコードレベルで正確に理解している人は意外と少ない。
まずは定義を見てみよう。TypeScriptの内部(`lib.es5.d.ts`)では、こいつは以下のように極めてシンプルに実装されている。
type NonNullable
おっと、ここで条件付き型(Conditional Types)の登場だ。
「もし `T` が `null` または `undefined` に割り当て可能(extends)なら `never` にし、そうでなければそのまま `T` を返す」という非常にシンプルな仕組みになっている。
`never` 型というのは、いわば「存在し得ない値」を示す底の型だ。Union型の中に `never` が混ざると、TypeScriptの型システムはそれを綺麗に消去してくれる(Distributive Conditional Typesの恩恵だ)。
例えば、次のような型があったとしよう。
type MaybeString = string | null | undefined;
// NonNullableを適用すると…
type DefiniteString = NonNullable
これによって、`null` や `undefined` という「不確実なノイズ」をきれいにパージし、「値が絶対に存在すること」を型レベルで保証できるようになるわけだ。
—
2. ブラウザの裏側とTypeScriptのランタイムの現実
さて、ここで少し視点を変えて、ブラウザやJavaScriptのランタイムの裏側の話をしよう。
TypeScriptは、あくまでの「開発時の静的解析ツール」だ。コンパイル(トランスパイル)されて生成されるJavaScriptのコードには、型情報は1バイトたりとも残らない。
つまり、`NonNullable
じゃあ何の意味があるのか?
それは、「開発者の脳内にある前提条件を、TypeScriptのコンパイラと完全に共有するため」にある。
例えば、バックエンドから送られてくるJSONデータには、たまに設計書に書いていない `null` が混ざってきたりする。ブラウザは `TypeError: Cannot read properties of null` を吐いて盛大にクラッシュする。
ここで `NonNullable
—
3. 【実践】現場ですぐに使えるユースケースとサンプルコード
理屈はこれくらいにして、実際のフロントエンド開発でどう使うのか、コピペしてそのまま読める実用的なサンプルコードを見ていこう。
ユースケース A: APIレスポンスの配列から「空っぽの要素」を根絶する
これが実務で一番多いパターンだ。APIから取得したユーザーリストの中に、退会済みなどで `null` が混ざっているケースを考えてみよう。
// サーバーから返ってくる生データの型
interface User {
id: string;
name: string;
}
type RawUserResponse = (User | null | undefined)[];
// 🔴 従来の泥臭い書き方
// 配列を回すたびに `user?.name` と書く必要があり、冗長になる
const rawUsers: RawUserResponse = [
{ id: “1”, name: “Alice” },
null,
{ id: “2”, name: “Bob” },
undefined,
];
// 🟢 NonNullableを活用したスマートなアプローチ
// filterの型ガード(User is User)と組み合わせるのが実務の定石だが、
// 単に確実な値だけを抽出したい場合は NonNullable が火を吹く
const cleanUsers: NonNullable
(user): user is NonNullable
);
// これで cleanUsers の型は User[] になり、以降の処理で余計なオプショナルチェイニングが不要になる!
cleanUsers.forEach((user) => {
console.log(user.name.toUpperCase()); // 安全にアクセス可能!
});
ユースケース B: フォームのバリデーション結果の型安全なマッピング
ReactやVueで複雑なフォームを扱う際、各フィールドの状態を管理するオブジェクトを考えてみよう。ユーザーがまだ入力していない状態は `null` や `undefined` だが、確認画面や送信フェーズでは「すべて値が埋まっていること」が前提になる。
// フォームの入力値の型(最初は空なので nullable)
interface FormValues {
username: string | null;
email: string | null;
age: number | undefined;
}
// 送信データ用の型を NonNullable で一括生成する
// Mapped Types と組み合わせることで、元のインターフェースを維持したまま中身だけを厳格化できる
type ValidatedFormValues
[K in keyof T]: NonNullable
};
type FinalPayload = ValidatedFormValues
/
Generated Type:
{
username: string;
email: string;
age: number;
}
/
// 実務での送信処理関数
function submitForm(data: FinalPayload) {
// ここに到達した時点で、値が欠損していないことが型レベルで100%保証されている
console.log(`Submitting to: ${data.email}, User: ${data.username}`);
}
// 実際の使い方
const currentForm: FormValues = {
username: “Taro Dev”,
email: “taro@example.com”,
age: 28,
};
// バリデーションを通過したと仮定してキャスト、あるいはアサーションを行う場合
if (currentForm.username && currentForm.email && currentForm.age !== undefined) {
// ここで自動的に型が絞り込まれるが、ユーティリティ型として強制したい場合にもNonNullableは有用
submitForm(currentForm as FinalPayload);
}
—
4. シニアから後輩へ贈る、実務でのアンチパターンと注意点
最後に、現場でやりがちな「危うい使い方」について釘を刺しておこう。
1. ランタイムの現実逃避に使わない
`NonNullable
型は「現実」に合わせるものであり、現実を型にねじ曲げてはいけない。
2. Zodなどのバリデーションライブラリとの使い分け
現代のフロントエンド開発では、外部入力をランタイムで安全に検証するために `Zod` や `Valibot` などのスキーマバリデーションを使うことが多い。UIコンポーネント内の厳密な型変換には `NonNullable
—
まとめ
- `NonNullable
` は、型 `T` から `null` と `undefined` を排除し、「確実に値が存在する状態」を作るための強力なユーティリティ型。 - コンパイル時の静的解析をリッチにするものであり、ランタイムの安全性を担保するためには適切なガードやバリデーションと併用する必要がある。
- 配列のフィルタリングや、フォームデータの確定フェーズなど、「ここから先は絶対に値がある」と断言できるシーンで使うと、コードの可読性が劇的に向上する。
型定義を制する者は、フロントエンドの保守性を制す。
ぜひ今日のコードから `NonNullable

コメント