Skip to content

@raw_response_type generates TypeScript types with duplicated union arms #5090

@jwatzman

Description

@jwatzman

The following is distilled down from a real-world example. In my app, I have Posts, which live inside a Group. I have a Linkable interface for types for which my app knows how to generate a URL. For a Group, we need to know the group type to put into the URL. For a Post, we need to know the Group it is in, as well as that Group type. A React component uses a fragment to fetch this data on anything that implements Linkable. When I create a Post, I want to link both to that Post and the Group that it's in, and so I spread my fragment on both.


Here's a very simplified version of my schema:

interface Linkable {
  id: ID!
}

type Post implements Linkable {
  id: ID!
  group: Group!
}

type Group implements Linkable {
  id: ID!
  type: String!
}

type Query {
  post: Post!
}

type Mutation {
  createPost: Post!
}

Here's a mutation using @raw_response_type to create a post, including the two fragment spreads so I can link to both places:

graphql`
  mutation TestMutation @raw_response_type {
    createPost {
      ...TestLinkableFragment
      group {
        ...TestLinkableFragment
      }
    }
  }
`;

graphql`
  fragment TestLinkableFragment on Linkable {
    id
    ... on Post {
      group {
        id
        type
      }
    }
    ... on Group {
      type
    }
  }
`;

The raw type that Relay generates for this is:

export type TestMutation$rawResponse = {
  readonly createPost: {
    readonly __isLinkable: "Post";
    readonly group: {
      readonly __isLinkable: "Post";
      readonly group: {
        readonly id: string;
        readonly type: string;
      };
      readonly id: string;
      readonly type: string;
    } | {
      readonly __isLinkable: "Group";
      readonly id: string;
      readonly type: string;
    } | {
      readonly __isLinkable: "Group";
      readonly id: string;
      readonly type: string;
    };
    readonly id: string;
  } | {
    readonly __isLinkable: "Post";
    readonly group: {
      readonly __isLinkable: "Post";
      readonly group: {
        readonly id: string;
        readonly type: string;
      };
      readonly id: string;
    } | {
      readonly __isLinkable: "Group";
      readonly id: string;
    } | {
      readonly __isLinkable: "Group";
      readonly id: string;
      readonly type: string;
    };
    readonly id: string;
  } | {
    readonly __isLinkable: "Group";
    readonly group: {
      readonly __isLinkable: "Post";
      readonly group: {
        readonly id: string;
        readonly type: string;
      };
      readonly id: string;
    } | {
      readonly __isLinkable: "Group";
      readonly id: string;
    } | {
      readonly __isLinkable: "Group";
      readonly id: string;
      readonly type: string;
    };
    readonly id: string;
    readonly type: string;
  };
};

Note all of the places that types like this are generated:

    {
      readonly __isLinkable: "Group";
      readonly id: string;
    } | {
      readonly __isLinkable: "Group";
      readonly id: string;
      readonly type: string;
    }

When using the raw response type for an optimistic response (a common use-case), it's easy to forget to add the type, since TS will match the first arm of that!

It seems like a bug that two arms of a union would be generated with the same __isLinkable, since Relay tends to flatten these out in most other cases?

Honestly it would be even better if Relay could pick the correct arm of TestLinkableFragment, since we know that the type is a Group!.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions