型の絞り込み(narrowing)の仕組みを習得し、独自の述語を書いてユニオンをより信頼できるものにします。
このレッスンを Kodokon で開くNarrowing(絞り込み) とは、TypeScript が制御フローを解析することで、広い型(ユニオンや unknown)をより正確な型へと洗練させていく仕組みです。あなたは意識せずにすでにこれを使っています。型付きの値に対するすべての if が解析を引き起こすからです。コンパイラは組み込みのガードを 3 系統追跡します。プリミティブに対する typeof、クラスインスタンスに対する instanceof、プロパティの存在を調べる in です。これらを適切に選ぶことで、検証を導くどころか無効にしてしまう as による無謀なキャストを避けられます。
function formatValue(
value: string | number | Date
): string {
if (typeof value === "string") {
return value.trim();
}
if (value instanceof Date) {
return value.toISOString();
}
return value.toFixed(2);
}in 演算子は、判別用のフィールドを持たない構造的に異なるオブジェクトを扱うときにとても役立ちます。ただし注意が必要です。in はキーの「存在」を(継承されたものも含めて)テストするのであって、その型をテストするわけではありません。外部データ(API や JSON)に対しては、実行時に実際に存在するものではなく、あなたの宣言に従って絞り込みます。現場での典型的な落とし穴は、typeof value === "object" のチェックが null を通してしまうことです。JavaScript では typeof null が "object" だからです。必ず明示的な null チェックを追加しましょう。
interface Cat { meow(): void }
interface Dog { bark(): void }
function speak(animal: Cat | Dog): void {
if ("meow" in animal) {
animal.meow();
} else {
animal.bark();
}
}ガードのロジックが再利用可能になったり、フロー解析には複雑すぎたりする場合は、カスタム型ガード を書きましょう。返り値の型を value is T と注釈した関数です。すると、コンパイラはあなたの実装を信頼します。これは契約です。チェックが不完全なら、型システムが決して気づかない実行時エラーを再び持ち込むことになります。TypeScript 5.5 以降、filter などのコールバックでは単純な述語が自動的に推論されますが、エクスポートされる関数では明示的な注釈が依然として標準です。
interface User { id: string; email: string }
function isUser(value: unknown): value is User {
return (
typeof value === "object" &&
value !== null &&
"id" in value &&
typeof value.id === "string" &&
"email" in value &&
typeof value.email === "string"
);
}
const raw: unknown =
JSON.parse('{"id":"1","email":"a@b.c"}');
if (isUser(raw)) {
console.log(raw.email.toLowerCase());
}typeof value === "object" のチェックだけでは、非 null のオブジェクトへの絞り込みには不十分なのですか?booleanvalue as Uservalue is Userasserts value