Skip to content

[TypeScript Resolvers] feat: Generate Interface types without field resolvers #2194

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Tracked by #8296 ...
rynobax opened this issue Jul 18, 2019 · 15 comments
Open
Tracked by #8296 ...
Assignees
Labels
core Related to codegen core/cli kind/enhancement New feature or request plugins

Comments

@rynobax
Copy link

rynobax commented Jul 18, 2019

Is your feature request related to a problem? Please describe.
When attempting to generate types for an existing graphql server, I am having trouble getting the generated types for interfaces to match my implementation.

On my server I only specify a __resolveType resolver for my interface type, and let the implementing types provide the revolvers for the shared fields. However, when I generate types, the interface type requires that a resolver for all fields be provided.

Example
interface Animal {
  color: String!
}

type Dog implements Animal {
  color: String!
  bark: String!
}

type Cat implements Animal {
  color: String!
  meow: String!
}
const resolvers: Resolvers = {
  Cat: {
    color: e => e.color,
    meow: e => e.noise,
  },
  Dog: {
    color: e => e.color,
    bark: e => e.noise,
  },
  Animal: {
    __resolveType: e => (e.dog ? "Dog" : "Cat")
    // Property 'color' is missing in type '{ __resolveType: () => "Cat"; }' but required in type 'AnimalResolvers<any, Animal>'
  },

Describe the solution you'd like

I would like to specify in my configuration that I would like interface resolver types to only require a __resolveType field.

I would be happy to work on adding the functionality if I could get some confirmation this is a feature worth adding, and not just me doing something wrong 😄 .

@kamilkisiela
Copy link
Collaborator

kamilkisiela commented Aug 12, 2019

I reproduced what you have within a test-case: 08ded80

Could you tell me how your codegen.yml looks like? Or could you even update the test to match what you have in your project?

@dotansimha
Copy link
Owner

As @kamilkisiela said, the generated signature doesn't force you to implement the resolvers for the interface's fields.

@dotansimha dotansimha added the waiting-for-answer Waiting for answer from author label Aug 12, 2019
@rynobax
Copy link
Author

rynobax commented Aug 12, 2019

Full codegen.yml:

schema: src/graphql/schema.graphql
generates:
  ./src/types/graphql.ts:
    plugins:
      - typescript
      - typescript-resolvers
    config:
      rootValueType: undefined
      contextType: ~/graphql/context#Context
      maybeValue: T | null | undefined
      avoidOptionals: true
      showUnusedMappers: true
      noSchemaStitching: true
      skipTypename: true
      enumsAsTypes: true
      namingConvention:
        enumValues: keep
      mappers:
        // Lots of mappers

I'm guessing it's due to avoidOptionals?

I can take a look tonight and update the test to match what I want.

@dotansimha
Copy link
Owner

@rynobax yeah the avoidOptionals is causing it, you can remove it for the typescript-resolver plugin. If you need to for typescript plugin only, you can do:

schema: src/graphql/schema.graphql
generates:
  ./src/types/graphql.ts:
    plugins:
      - typescript:
		avoidOptionals: true
      - typescript-resolvers
    config:
      rootValueType: undefined
      contextType: ~/graphql/context#Context
      maybeValue: T | null | undefined
      showUnusedMappers: true
      noSchemaStitching: true
      skipTypename: true
      enumsAsTypes: true
      namingConvention:
        enumValues: keep
      mappers:
        // Lots of mappers

@rynobax
Copy link
Author

rynobax commented Aug 15, 2019

What I want is to have avoidOptionals on for all my object resolvers, but not my interface or union resolvers. Alternatively, it would also be fine if the interfaces types worked the same as union types, where only a __resolveType field is generated..

I looked into adding a test for what I want, but I had trouble getting the workspaces to bootstrap. If it would be useful I can keep working on it. I would also be happy to work on a PR for this feature if you want, just let me know.

@dotansimha
Copy link
Owner

@rynobax Make sure you have the latest Yarn installed, clone this repo and run: yarn and then yarn build. This should get you started.

The exact use-case you mentioned is a bit different, so I'm closing this one, and feel free to open a new issue that described exactly what you need and what options should avoidOptionals should support.

@mscharley
Copy link

@dotansimha this is an example of what was originally asked for. The interaction between using interface graphql types and the avoidOptionals configuration option is unfortunate and ends up causing a lot of duplicated code since the same resolvers need to be implemented twice instead of defining a __resolveType once then implementing the resolvers for each implementation of the interface.

@rynobax
I managed to patch this using a mapped type, but it'd be nice to get a config option to generate __resolveType as the only resolver for interface types:

export type FixTypeResolvers<Resolvers> = {
  [P in keyof Resolvers]:
    Resolvers[P] extends { __resolveType: (a: any, b: any, c: any) => any }
      ? Pick<Resolvers[P], "__resolveType">
      : Resolvers[P];
};

@mscharley
Copy link

Or put another way, I'd like a way to make the following SDL:

interface View {
  "The type of view."
  type: ViewKind!
  "A list of sites for this view."
  sites: [Site!]!
}

generate the following typings:

export type ViewResolvers<
  ContextType = Context,
  ParentType = ResolversParentTypes["View"]
> = {
  __resolveType: TypeResolveFn<
    "ClientView" | "SiteView",
    ParentType,
    ContextType
  >;
};

Without turning off avoidOptionals so that all my other resolvers don't allow optional resolvers.

@dotansimha
Copy link
Owner

@mscharley I see, this use-case seems fine. If you already managed to change that, maybe you can create a PR for this with a configuration flag? :)

@mscharley
Copy link

I haven't made any changes to the code generation, I just made a mapped type inside my application which manages to patch in the desired behaviour. If you're happy that this is an actual issue, can you reopen this please?

@dotansimha dotansimha removed the waiting-for-answer Waiting for answer from author label Aug 31, 2019
@dotansimha
Copy link
Owner

Keeping open, thanks @mscharley . Can you please provide an example for the type you created? maybe it's something we can use in the generated output.

@dotansimha dotansimha reopened this Aug 31, 2019
@mscharley
Copy link

I'm not sure what you're asking for here, I provided an example SDL for an interface and what I'd want the code generation to output already a few comments back. I also provided the mapped type already too, though that's very much a hack.

@dotansimha
Copy link
Owner

dotansimha commented Aug 31, 2019

I see. I wanted to add it to the codegen core as configuration, in order to help you to avoid the need to wrap the type. That's why I opened it. If you are fine with the current status, I can close it.

@mscharley
Copy link

I would love for this to be a configuration option for this, I'm just confused by what you meant by "Can you please provide an example for the type you created?". I'm pretty sure I already provided examples, so I'm not clear on what you're asking for.

@dotansimha
Copy link
Owner

dotansimha commented Sep 2, 2019

Thanks @mscharley , I wrote it because I missed your comment with the example :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
core Related to codegen core/cli kind/enhancement New feature or request plugins
Projects
None yet
Development

No branches or pull requests

5 participants