dotansimha / graphql-code-generator

A tool for generating code based on a GraphQL schema and GraphQL operations (query/mutation/subscription), with flexible support for custom plugins.
https://the-guild.dev/graphql/codegen/
MIT License
10.78k stars 1.31k forks source link

Aliased attributes could optionnaly be added to the Object Type #4898

Open such opened 3 years ago

such commented 3 years ago

Is your feature request related to a problem? Please describe.

I'm using codegen with the @homebound/graphql-typescript-factories plugin in order to automatically generate factories for my tests in my React app. My components have their props typed thanks to a fragment they expose. When an attribute is aliased in the fragment it appears as an attribute in the requested type:

fragment ClubAvatar_club on Club {
  name
  avatarUrl: pictureUrl(derivative: "avatar")
}

gives:

export interface ClubAvatar_club {
  __typename: "Club";
  name: string;
  avatarUrl: string | null;
}

The issue is that the aliased attribute (avatarUrl here) does not appear in the codegen generated Club type:

export type Club = {
  __typename: 'Club';
  pictureUrl: Maybe<Scalars['String']>;
};

and hence my factory generates a Club object that lacks the avatarUrl attribute. I cannot pass that object to my Component without typescript complaining (for a good reason).

Describe the solution you'd like

Thanks to the typescript-operations plugin, my fragments can be parsed and those aliased attributes can be found. Would it be possible to add an option so that aliased attribute are added to the generated type?

dotansimha commented 3 years ago

Hi @such ! This is a great idea, but I'm not sure we can add those attributes, since they are not being represented by result TypeScript type. If you'll use GraphQL variables, you will be able to set those, but this might be an issue since fragments variables are still experimental in GraphQL. Maybe this is something @homebound/graphql-typescript-factories should be doing regardless of codegen?

such commented 3 years ago

Hi! My first instinct was to ask @homebound/graphql-typescript-factories but I then realized they base their factories on the type and the type is generated by graphql-code-generator/typescript. But I'll post an issue there anyway to start the conversation.

One thing though, you're mentioning fragment variables so I'm not sure my request was very clear. What I want is just that the Club type look like:

export type Club = {
  __typename: 'Club';
  pictureUrl: Maybe<Scalars['String']>;
  avatarUrl: Maybe<Scalars['String']>;
};
dotansimha commented 3 years ago

@such EDIT: I see now, my bad, I was thinking this is something else.

I don't think generating both fields is the right way to go, since aliasing means that the response will have the aliased type and not the original name. The schema itself can't know what are all the possible fields that could be aliased and why, so we can't change the definition of Club because of the way it's being consumed.

such commented 3 years ago

That's why I was mentioning typescript-operations actually. In the context of the project, by parsing fragments, it's possible to know all the fields that are aliased. And I think both fields should be present since the field will not always be aliased (BTW it's aliased in my case to prevent clashing, because both "fields" are actually present in the response).

dotansimha commented 3 years ago

I see, but fragments / operations have selection sets, and they are able to choose the fields and alias those. The schema has nothing to do with that. type Club comes from the typescript and it's based on the schema. We can't change the generated type that is based on the schema, according to the way it's being consumed.

If you ask for pictureUrl, you are going to get a field called pictureUrl, and if you are aliasing it to avatarUrl, you are going to get only avatarUrl, without pictureUrl. Adding both fields means that in runtime, one of them will be undefined for sure, because it's not accurate and not representing the actual data structure.

Please note that GraphQL schema provides the entire set of service and abilities that your API has, but selection set represents the fields you ask for. You might change the name and alias some of them, but it can't effect the schema itself. GraphQL Codegen aims to generate exactly the structure provided by the schema, and the structure provided by the selection set of the operations. I can't see a valid use case of changing the schema types, based on the operation selection set, due to field aliasing.

estyxx commented 1 year ago

Hey, I have the same issue: I'm using some alias in the queries and as the types don't include the aliases, typescript is throwing types errors... is there a workaround? I'm using typescript and typescript-operations

stephenh commented 1 year ago

Well, a few years later, but fwiw we just added support for aliases in our graphql-typescript-response-factories, which does use the OperationDefinition's SelectionSet to know which fields have been aliased, albeit only for the top-most level set of fields in a GraphQL response:

https://github.com/homebound-team/graphql-typescript-response-factories/pull/20

However it doesn't handle fragments in general, so we've filed this issue to potentially implement:

https://github.com/homebound-team/graphql-typescript-factories/issues/54