Skip to content

TypeScript の型の絞り込み 解説

TypeScript が分岐の中で型を絞り込む方法を、テクニック別にまとめました。組み込みガード、カスタム述語、アサーション関数、判別可能なユニオン、等価性と truthiness のフロー、そしてエイリアスと遅延の絞り込みです。各行はテクニックと 1 行の例を組み合わせます。

絞り込み(narrowing)とは、TypeScript がコード分岐の中で広い型をより狭い型へ変え、プロパティアクセスが安全であることを証明できるようにする仕組みです。各チェック(typeof、in、述語、判別子)はコンパイラに「ここから先、この値はより具体的だ」と伝えます。以下のテクニックが実用セットです。

リファレンステーブル · 23 項目
23 of 23 rows
組み込みガード
プリミティブを絞り込みます。string、number、boolean など。if (typeof x === 'string') x.toUpperCase()
クラスまたは構築可能なインスタンスに絞り込みます。if (e instanceof Error) e.message
プロパティが値に存在するかどうかで絞り込みます。if ('id' in obj) obj.id
任意の値を配列に絞り込みます。ユニオンは配列のメンバーに絞り込まれます。if (Array.isArray(x)) x.map(f)
カスタムガード
戻り値が引数を絞り込む述語関数です。function isUser(x): x is User { return !!x.id }
述語は単一の型だけでなく、型のユニオンにも絞り込めます。function isKey(x): x is string | number
交差型の述語は入力型を保ったまま、そこにフィールドを追加します。function hasId(x): x is x & { id: string }
x がその型でなければ例外を投げるアサーション関数です。function assertUser(x): asserts x is User
判別可能なユニオン
switch や if で、共通のリテラルフィールドによってユニオンを絞り込みます。type E = { kind: 'a'; a: number } | { kind: 'b'; b: string }
各 case はユニオンの 1 つのメンバーに絞り込みます。switch (u.kind) { case 'a': u.a }
プロパティが 1 つのメンバーにしか存在しない場合に、タグなしユニオンを絞り込みます。if ('b' in u) u.b
各 case は条件式です。マッチした case が値を絞り込みます。switch (true) { case typeof x === 'string': }
default ケースで never に代入することで、switch が網羅的であることを証明します。default: { const x: never = u }
等価性とフロー
等価性のチェック後に null と undefined を除外します。if (x === null) return; x.toFixed()
falsy な値(0、''、null、undefined、NaN)を除外します。if (!x) return; // x is defined here
truthy な連鎖プロパティは、ベースとなる値が存在することを証明します。if (user?.active) user.name
非 null アサーションはチェックなしで null と undefined を除外します。x!.toFixed(2)
definite assignment:x は読み取られる前に代入されると宣言します。let name!: string; init()
絞り込みは分岐の中と早期 return の後にも適用されます。if (!arr) return; arr.map(...)
エイリアスと遅延
チェックを保持する const boolean は、評価されるどこでも絞り込みます。const isStr = typeof x === 'string'; if (isStr) …
const 変数は、それをキャプチャするクロージャの中で絞り込みを保持します。const s = get(); if (s) use(() => s.length)
TS 5.4 以降、let や引数は最後の代入の後もクロージャ内で絞り込みを保持します。let n: string | null = get(); if (n) use(() => n.length)
unknown は、ガードが実際の型に絞り込むまで使用できません。if (typeof u === 'string') u.length