私が歌川です

@utgwkk が書いている

Relay + TypeScriptでnodeクエリを使うときは__typenameフィールドを明示的に取得すると便利

タイトルが全てです。

作品詳細ページで、id をもとに作品 (Artwork) を取得したいとする。 ArtworkNode インタフェースを実装しているなら、以下のように node クエリを使って取得できる。

query ArtworkDetailQuery($id: ID!) {
  artwork: node(id: $id) {
    ... on Artwork {
      id
      title
      caption
    }
  }
}

が、このクエリをもとにrelay-compilerでTypeScriptのコードを生成すると、以下のように artwork のフィールドが全てoptionalになった型定義が生成されてしまい、不便である。

export type ArtworkDetailQueryResponse = {
    readonly artwork: {
        readonly id?: string;
        readonly title?: string;
        readonly caption?: string;
    } | null;
};

こういう場合は、node クエリで取得する Node オブジェクトの __typename フィールドを取得すればよい。

query ArtworkDetailQuery($id: ID!) {
  artwork: node(id: $id) {
    __typename
    ... on Artwork {
      id
      title
      caption
    }
  }
}

こうするとrelay-compilerが以下のようなunion typeを出力してくれる。使う際は __typename で型を絞り込むことができる。

export type ArtworkDetailQueryResponse = {
    readonly artwork: ({
        readonly __typename: "Artwork";
        readonly id: string;
        readonly title: string;
        readonly caption: string;
    } | {
        /*This will never be '%other', but we need some
        value in case none of the concrete values match.*/
        readonly __typename: "%other";
    }) | null;
};
const { artwork } = usePreloadedQuery(...);

if (!(artwork && artwork.__typename === "Artwork")) {
    return <div>artwork not found</div>;
}
// artworkはArtwork型のオブジェクトであることが確定する

このコンパイラの挙動はrelay-compiler 11.0.2で確かめた。めっちゃフィールドがoptionalになるけどなんとかしたいね、と思って試しに __typename を取得してみたらこの挙動に気づいた。