facebook / relay

Relay is a JavaScript framework for building data-driven React applications.
https://relay.dev
MIT License
18.36k stars 1.82k forks source link

[relay-compiler] Unexpected type/__typename for fragments on interfaces #4439

Open joeried opened 1 year ago

joeried commented 1 year ago

Hello,

I have a case here where the typescript types generated by the relay-compiler look wrong to me.

Let's say I've got the following schema

interface TestInterface implements Node{
    id: ID!

    fieldA: String!
}

type TypeWithFieldB implements Node & TestInterface {
    id: ID!

    fieldA: String!
    fieldB: String!
}

type TypeWithFieldC implements Node & TestInterface {
    id: ID!

    fieldA: String!
    fieldC: String!
}

Example 1 (which works like I would expect it)

If I now create a fragment like this

useFragment(graphql`fragment Test on TestInterface {
    ... on TypeWithFieldB {
        __typename
        fieldA
        fieldB
    }
    ... on TypeWithFieldC {
        __typename
        fieldA
        fieldC
    }

}`, null)

Then the generated types look good

export type Test$data = {
  readonly __typename: "TypeWithFieldB";
  readonly fieldA: string;
  readonly fieldB: string;
  readonly " $fragmentType": "Test";
} | {
  readonly __typename: "TypeWithFieldC";
  readonly fieldA: string;
  readonly fieldC: string;
  readonly " $fragmentType": "Test";
}

Example 2 (Wrong __typename)

If I create a fragment like this (fieldA pulled to the top):

useFragment(graphql`fragment Test2 on TestInterface {
    fieldA

    ... on TypeWithFieldB {
        __typename
        fieldB
    }
    ... on TypeWithFieldC {
        __typename
        fieldC
    }

}`, null)

Then the generated types look like this:

export type Test2$data = {
  readonly __typename: "TypeWithFieldB";
  readonly fieldA: string;
  readonly fieldB?: string;
  readonly fieldC?: string;
  readonly " $fragmentType": "Test2";
};

While the type itself looks like a sensible merge of TypeWithFieldB and TypeWithFieldC the __typename should probably not be "TypeWithFieldB".

Example 3 (Wrong __typename caused by refetchable fragment)

useRefetchableFragment(graphql`fragment Test3 on TestInterface @refetchable(queryName: "Test3RefetchQuery")  {
    ... on TypeWithFieldB {
        __typename
        fieldA
        fieldB
    }
    ... on TypeWithFieldC {
        __typename
        fieldA
        fieldC
    }

}`, null)

Then the generated types look like in Example 2. What is surprising to me here is that the fragment structure is chosen exactly as in the working example 1 with the only difference that here the fragment is marked as refetchable.


Repo with example code: https://github.com/joeried/relay-compiler-issue-repro/

If I'm not mistaken, then the generated types are just wrong, or am I mistaken? Is there a way to get types that are structured more like the ones in Example 1?

zawa1205 commented 2 months ago

I'm experiencing similar things.

#This schema is as same as joeried's one

interface TestInterface implements Node{
    id: ID!

    fieldA: String!
}

type TypeWithFieldB implements Node & TestInterface {
    id: ID!

    fieldA: String!
    fieldB: String!
}

type TypeWithFieldC implements Node & TestInterface {
    id: ID!

    fieldA: String!
    fieldC: String!
}
fragment Test4 on TestInterface {
    __typename
    ... on TypeWithFieldB {
        fieldA
        fieldB
    }
    ... on TypeWithFieldC {
        fieldA
        fieldC
    }
}

The resulting type after executing the above query is as follows:

type TestQueryResult = {
    readonly __typename: string;
}

I hope to be of the following type:

type TestQueryResult = {
    readonly __typename: "TypeWithFieldB" | "TypeWithFieldC";
}
Quramy commented 2 months ago

@captbaritone Are there any plans to fix this issue?