Is your feature request related to a problem? Please describe.
Adding implements InterfaceName to a type is not a breaking change (according to GraphQL Inspector, and I think community consensus) but it does mean that client behavior could change. I usually recommend that clients program defensively with a default: branch it their switch statements, like this:
query {
pet {
name
... on Cat {
meow
}
... on Dog {
bark
}
}
}
switch (pet.__typename) {
case 'Cat':
console.log(`Cat meows ${pet.meow}`);
break;
case 'Dog':
console.log(`Dog barks ${pet.bark}`);
break;
default:
console.log(`Unknown animal ${pet.__typename}`);
}
So that when type Fish implements Pet is added to the schema, the UI has some way of handling it.
However, the typescript-operations plugin generates types like this:
export type PetQuery = {
__typename: "Query";
pet:
| { __typename: 'Cat'; meow?: string | null; name?: string | null; }
| { __typename: 'Dog'; bark?: string | null; name?: string | null; }
};
Which results in a TypeScript error:
default:
console.log(`Unknown pet type ${pet.__typename}`);
// ^^^^^^^^^^
// Property '__typename' does not exist on type 'never'
Describe the solution you'd like
I'd like an option to the typescript-operations plugin that adds an additional type to the union type like this:
export type PetQuery = {
__typename: "Query";
pet:
| { __typename: 'Cat'; meow?: string | null; name?: string | null; }
| { __typename: 'Dog'; bark?: string | null; name?: string | null; }
| { __typename: string, name?: string | null }
};
The option would be disabled by default so that users could opt into this change.
Describe alternatives you've considered
You can always add // @ts-ignore in the default: clause, or cast it to any (thought this might trigger other linters):
console.log(`Unknown pet type ${(pet as any).__typename}`);
Additional context
Ideally we could enforce adding the default: class with the switch-exhaustiveness-check eslint rule, but it only works on union types. I played around with something like
enum PetQuery_PetTypename {
Cat = "Cat",
Dog = "Dog",
__unknown = ""
}
export type PetQuery = {
__typename: "Query";
pet:
| { __typename: PetQuery_PetTypename.Cat; meow?: string | null; name?: string | null; }
| { __typename: PetQuery_PetTypename.Dog; bark?: string | null; name?: string | null; }
| { __typename: PetQuery_PetTypename.__unknown, name?: string | null }
};
which did cause the exhaustiveness check to occur, but it feels hacky. But I bet someone with more TypeScript knowledge knows how to do this!
Is your feature request related to a problem? Please describe.
Adding
implements InterfaceNameto a type is not a breaking change (according to GraphQL Inspector, and I think community consensus) but it does mean that client behavior could change. I usually recommend that clients program defensively with adefault:branch it theirswitchstatements, like this:So that when
type Fish implements Petis added to the schema, the UI has some way of handling it.However, the
typescript-operationsplugin generates types like this:Which results in a TypeScript error:
Describe the solution you'd like
I'd like an option to the
typescript-operationsplugin that adds an additional type to the union type like this:The option would be disabled by default so that users could opt into this change.
Describe alternatives you've considered
You can always add
// @ts-ignorein thedefault:clause, or cast it toany(thought this might trigger other linters):Additional context
Ideally we could enforce adding the
default:class with theswitch-exhaustiveness-checkeslint rule, but it only works on union types. I played around with something likewhich did cause the exhaustiveness check to occur, but it feels hacky. But I bet someone with more TypeScript knowledge knows how to do this!