質問の歴史:
'in'オペレーターはJavaScriptからTypeScriptに引き継がれ、オブジェクト内のプロパティの存在を確認するために広く使用されています。TypeScriptでは、型システムのために、そのセマンティクスが追加の意味を持ちます。特にオプショナルプロパティの場合、単に値の存在だけでなく、型レベルでの宣言も重要です。
問題:
多くのTypeScript開発者は、undefinedのシンプルなチェックとオブジェクト内でのプロパティの存在確認を混同してしまいます。プロパティが存在していても、その値がundefinedである場合や、プロパティがオブジェクトに直接定義されていない場合にエラーが発生する可能性があります。
解決策:
'in'オペレーターは、プロパティがオブジェクトのプロトタイプチェーンに実際に存在するかどうかを、その値に関係なく確認します。これはオプショナルまたは計算されたプロパティを検証する際に重要であり、falsy値の予期しない処理を避けることができます:
コードの例:
interface User { id: number; name?: string; } const user1: User = { id: 1 }; const user2: User = { id: 2, name: undefined }; console.log('name' in user1); // false console.log('name' in user2); // true
重要な特徴:
undefinedのチェック(obj.prop !== undefined)と'in'の使用はどのように異なりますか?
undefinedのチェックは、値だけを確認し、プロパティの存在を確認しません。もしプロパティが存在するがその値がundefinedの場合、結果はtrueになります。もしプロパティがまったく存在しない場合、その値もundefinedですが、セマンティックには異なるケースです。
例:
const obj: any = { foo: undefined }; console.log('foo' in obj); // true console.log(obj.foo !== undefined); // false console.log('bar' in obj); // false console.log(obj.bar !== undefined); // false
'in'オペレーターは、プロパティの存在をオブジェクト自体のみに限定して、プロトタイプチェーンを無視できますか?
いいえ、標準の'in'はプロトタイプもチェックします。自身のプロパティのみを確認するには、hasOwnPropertyメソッドを使用します:
const obj = Object.create({ foo: 123 }); obj.bar = 456; console.log('foo' in obj); // true console.log(obj.hasOwnProperty('foo')); // false
'in'オペレーターを使ってTypeScriptのユニオン型の型細分化を行うことはできますか?
はい、TypeScriptでは'in'は識別されたユニオンを思わせるもので、特定のバリエーションに型を絞ることができます:
type Shape = { kind: 'circle'; radius: number } | { kind: 'square'; size: number }; function getArea(shape: Shape) { if ('radius' in shape) { // ここでshapeは円です return Math.PI * shape.radius ** 2; } // ここでshapeは正方形です return shape.size ** 2; }
ユーザーの配列を持つプロジェクトで、user.name !== undefinedを使用してオプショナルフィールドをチェックしたことがあり、これがundefinedとして明示的に設定されたユーザーに対して不正確なロジックをもたらしました。
利点:
欠点:
オプショナルフィールドの存在を確認するために'in'を使用することで、その値に関係なくフィールドの存在を正確に区別でき、"フィールドがない"と"フィールドはあるが値が指定されていない"を正しく区別できました。
利点:
欠点: