Description
Is your feature request related to a problem? Please describe.
The current treatment of interfaces treats an interface as a union of its types. This means it's actually not safe for backwards compatible usage (i.e., the server should be able to add new interface implementations in a backwards compatible way, but graphql-code-generator breaks that contract). It's not the end of the world, but I didn't see anything else about this.
Describe the solution you'd like
It's not possible to do something like
interface IAnimal { __typename: string; name: string; }
interface Dog extends IAnimal { __typename: "Dog"; favoriteDogTreatType: string; }
interface Cat extends IAnimal { __typename: "Cat"; meowVolume: number; }
since the resulting codegen would be something like this:
type Animal = IAnimal | Dog | Cat;
and this would be broken:
const animal: IAnimal = ...;
if (animal.__typename === "Dog") {
// This is an error since this type guard only narrows to (IAnimal | Dog)
// since it's technically valid for IAnimal to have __typename "Dog" in
// TypeScript's world -- even though that's not possible in GraphQL world
console.log(animal.favoriteDogTreatType);
}
One solution to this could be to use a sentinel value:
type Animal = (IAnimal & __typename: "_interface_sentinel_do_not_check_for_me_or_you_will_be_fired_") | Dog | Cat;
Describe alternatives you've considered
Status quo isn't the end of the world! :)
Additional context
The actual issues this can cause is essentially forgetting to write fallback code which then breaks when new interface implementations are added on the server side.
// This type-checks in the current version
function greetAnimal(animal: Animal): string {
switch (animal.__typename) {
case "Dog":
return `Hello, puppy!`;
case "Cat":
return `Meow meow.`;
}
}
This breaks when the server adds an Iguana
implementation because there's no default case.