Skip to content
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

Weird nested interface syntax generated for fragments? #76

Closed
steadicat opened this issue Jun 13, 2017 · 12 comments · Fixed by #162
Closed

Weird nested interface syntax generated for fragments? #76

steadicat opened this issue Jun 13, 2017 · 12 comments · Fixed by #162

Comments

@steadicat
Copy link

I’m seeing weird syntax in the exported TypeScript definitions when using fragments in queries. Interfaces are nested inside each other like this:

export interface Main {
  IFragmentSidebar
  windowState: SelectionOnWindowState;
  profile: SelectionOnProfile;
}

Is this intentional? I've never encountered this syntax before. The TypeScript compiler doesn't seem to know what to do with it. It doesn't throw an error but it seems like it interprets it as IFragmentSidebar: any;.

@brettjurgens
Copy link
Contributor

Nope, definitely not intended. Could you send over a reproduction schema & query so that I can take a look?

@steadicat
Copy link
Author

I can't find a quick way to test a schema + query, but you should be able to repro with a simple fragment spread. Something like this:

query Main {
  ...Sidebar
}

fragment Sidebar on Query {
}

// Schema
schema {
  query: Query
}

type Query {
}

@brettjurgens
Copy link
Contributor

we have tests that look like those and seem to work fine https://github.com/avantcredit/gql2ts/blob/master/test/from-query-normalSchema.js

how are you running this? gql2ts command line? from-query, from-schema? how are you invoking it?

need some more information and, ideally, a way to reproduce it

@BinaryMuse
Copy link

👋 Hi there! I'm running into something similar.


Input

GitHub's public GraphQL schema (schema.gql, link to demo repo below) and this sample query (query.gql):

query MyTestQuery {
  resource(url: "https://github.com/atom/atom") {
    __typename
    ... on Repository {
      id name owner { login }
    }
    ... on PullRequest {
      id title author { login }
    }
    ... on Issue {
      ... Issue
    }
    ... on Organization {
      ... Organization
    }
  }
}

fragment Issue on Issue {
  id title author { login }
}

fragment Organization on Organization {
  orgName:name
}
gql2ts gql/schema.gql gql/query.gql -o out/gql.d.ts

Output

export interface SelectionOnOwner {
  login: string;
}

export interface SelectionOnAuthor {
  login: string;
}

export interface SelectionOnResource {
  __typename: string;
  id?: string;
  name?: string;
  owner?: SelectionOnOwner;
  id?: string;
  title?: string;
  author?: SelectionOnAuthor | null;
  IFragmentIssue;
  IFragmentOrganization;
}

export interface MyTestQuery {
  resource: SelectionOnResource | null;
}


export interface IFragmentIssue {
  id: string;
  title: string;
  author: SelectionOnAuthor | null;
}


export interface IFragmentOrganization {
  orgName: string | null;
}

Note the duplicated id fields, as well as the fields directly on SelectionOnResource that don't belong to every possible resource, as well as the nested fragment interfaces that @steadicat was seeing.


You can find a ready-to-clone repository that demonstrates this behavior at https://github.com/BinaryMuse/gql2ts-bug; hope it helps.

@brettjurgens
Copy link
Contributor

Thanks @BinaryMuse I'll take a look!

@brettjurgens
Copy link
Contributor

@BinaryMuse based on the test repo I think I've narrowed down the problem to when speads are done like:

query MyTestQuery {
  resource(url: "https://github.com/atom/atom") {
    ... on Organization {
      ... Organization
    }
  }
}

fragment Organization on Organization {
  orgName:name
}

This is obviously valid, but I must have overlooked it.

I'm working on fixing this, but in the meantime you can amend your query to look like:

query MyTestQuery {
  resource(url: "https://github.com/atom/atom") {
    __typename
    ... Repository
    ... PullRequest
    ... Issue
    ... Organization
  }
}

fragment Issue on Resource {
  ... on Issue {
    id title author { login }
  }
}

fragment Organization on Resource {
  ... on Organization {
    orgName:name
  }
}

fragment Repository on Resource {
  ... on Repository {
    id name owner { login }
  }
}

fragment PullRequest on Resource {
  ... on PullRequest {
    id title author { login }
  }
}

I'm going to try to figure out a fix for this tonight, but I'll definitely have something out tomorrow

@brettjurgens
Copy link
Contributor

@BinaryMuse @steadicat would you mind trying against gql2ts@1.7.2-4. I think I've fixed the issue in #162.

@BinaryMuse
Copy link

@brettjurgens Thanks for the super fast response! I just tried it and it seems to have resolved the issues raised here. 🎉

I think there's an opportunity to generate stronger types in a case such as mine, but I'm happy to open a different issue for that if you prefer.

@brettjurgens
Copy link
Contributor

@BinaryMuse awesome! Not sure if we're talking about the same opportunity, but I noticed in the case where you add fragments on all possible cases for a union/interface, the types could be OR'd instead of Partial and AND'd. if that makes sense.

I also noticed that the __typename produces a string as the type, when it should really be the specific type, I'm going to open another issue for that.

if we're talking about different things, definitely open another issue! I'm putting together a list of things I'd like to get out for 2.0 (#163) and the more the merrier 😄

@BinaryMuse
Copy link

BinaryMuse commented Mar 22, 2018

I'm pretty sure we're talking about the same thing; in the example repo I set up, the generated types look something like

export interface MyTestQuery {
  resource:
    | SelectionOnResource &
        Partial<IFragmentIssue> &
        Partial<IFragmentOrganization>
    | null;
}

export interface IFragmentIssue {
  __typename: string;
  id: string;
  title: string;
  author: SelectionOnAuthor | null;
}

export interface IFragmentOrganization {
  __typename: string;
  orgName: string | null;
}

but since we know statically what all the typenames are, it could be something like

export interface MyTestQuery {
  resource:
    | IFragmentIssue
    | IFragmentOrganization
    | DefaultSelectionOnResource

export interface IFragmentIssue {
  __typename: "Issue";
  id: string;
  title: string;
  author: SelectionOnAuthor | null;
}

export interface IFragmentOrganization {
  __typename: "Organization";
  orgName: string | null;
}

export interface DefaultSelectionOnResource {
  __typename: string;
}

(where __typename has been rolled up from from the "selection" into all the possible values)

@brettjurgens
Copy link
Contributor

yep, seems like it's the same case. let me open another issue

@brettjurgens
Copy link
Contributor

@BinaryMuse opened #164 to track this. I tried to put some thoughts down towards the complexity of it, but I think, overall, that it's definitely doable (and would be a great win)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants