事象

ユーザによってはemailを持たない、という型定義をしたときに、hasOwnPropertyでは型ガードが効かずエラーになってしまった

type UserInfo = {
  username: string;
  attributes: GuestUserAttributes | PromotedUserAttributes;
};
type GuestUserAttributes = {
  sub: string;
};
type PromotedUserAttributes =
  | GuestUserAttributes
  | {
      email: string;
    };
{userInfo && userInfo.attributes.hasOwnProperty('email') && (
  <Text>{userInfo.attributes.email}</Text>
)}
 
//Property 'email' does not exist on type 'PromotedUserAttributes'.
//  Property 'email' does not exist on type 'GuestUserAttributes'.ts(2339)

解決

in演算子を使う

hasOwnPropertyは型ガードが実装されていないが、in演算子は型ガードとして動く。

{userInfo && 'email' in userInfo.attributes && (
  <Text>{userInfo.attributes.email}</Text>
)}

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#type-guards-inferred-from-in-operator

hasOwnPropertyを拡張する

あるいは、Object型のinterfaceを拡張してhasOwnPropertyに型ガードを実装する方法もある

// declare global { // need this declaration if in a module
interface Object {
  hasOwnProperty<K extends PropertyKey>(key: K): this is Record<K, unknown>;
}
// } // need this declaration if in a module

Why does Typescript treat object.hasOwnProperty("key") as essentially different from "key" in object