dotansimha / dotansimha/graphql-code-generator

TypeScript output shouldn't treat interface as a union of implementing types

Open
#5,869 3 comments 1 reaction 1 assignee Claimed by @charlypoly View on GitHub
core kind/enhancement stage/0-issue-prerequisites status/triage
Dominant language
TypeScript
Stars
11.3k
Forks
1.4k
Avg merge
1d 1h
Merged PRs (30d)
23

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

```ts
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:

```ts
type Animal = IAnimal | Dog | Cat;
```

and this would be broken:

```ts
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:

```ts
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.

```ts
// 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.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.