amount / gql2ts Goto Github PK
View Code? Open in Web Editor NEWconvert a graphql schema to a typescript definition
License: MIT License
convert a graphql schema to a typescript definition
License: MIT License
Let's say I have a schema something like this...
type Query {
user(userId: Int!): User!
}
type User { ... }
schema {
query: Query
}
I would like to generate the type definition of the actual resolver functions themselves for queries, mutations, and subscriptions.
interface IQuery {
user(parent: any, args: { userId: number }, context: any, info: any): User;
}
This would allow simple typechecking of resolvers (or other things)...
const query: IQuery = {
user(_, { userId }): User { /* ... */ }
}
This way if your schema file changes but someone forgets to update the resolvers, mutations, etc then you're covered.
Thoughts?
brought up by @zamiang here: #127 (comment)
essentially, in a query that looks like:
#import "./path/to/fragment"
query DoSomething {
...FragmentFromImport
}
should import the fragment & generate the appropriate typings
This would be a nice convenience feature.
add some test coverage for mutations
Hitting those duplicated module errors again 😞 Updating would solve it 👍
I used 1.0.2. The problem only occurs when I install gql2ts
with npm i gql2ts
without -g
option.
node_modules/@gql2ts/from-schema/dist/index.js:120
const f1 = type.getFields();
^
TypeError: type.getFields is not a function
at typeToInterface (/xxx/node_modules/@gql2ts/from-schema/dist/index.js:120:25)
at typeArr.filter.filter.map.type (/xxx/node_modules/@gql2ts/from-schema/dist/index.js:156:26)
at Array.map (native)
at typesToInterfaces (/xxx/node_modules/@gql2ts/from-schema/dist/index.js:156:14)
at run (/xxx/node_modules/@gql2ts/from-schema/dist/index.js:162:12)
at Object.exports.schemaToInterfaces (/xxx/node_modules/@gql2ts/from-schema/dist/index.js:164:63)
at Object.exports.generateNamespace (/xxx/node_modules/@gql2ts/from-schema/dist/index.js:167:85)
at run (/xxx/node_modules/gql2ts/dist/index.js:38:35)
at Socket.process.stdin.on (/xxx/node_modules/gql2ts/dist/index.js:54:35)
at emitNone (events.js:110:20)
There is an error in the genereateNamespace method on 'from-schema' in the Object.assign which causes the external language options to be ignored when an ES module is specificed.
This line of code:
https://github.com/avantcredit/gql2ts/blob/master/packages/from-schema/src/index.ts#L293
results in an call to Object.assign. However, due the ES6 module definition you will end up with overrides being something like
{
default: {},
DEFAULT_OPTIONS {...}
}
The code should do something like:
export const generateNamespace: GenerateNamespace = (namespace, schema, options = {}, overrides = {}) => {
const formatters: IFromQueryOptions = { ...DEFAULT_OPTIONS, ...overrides.DEFAULT_OPTIONS };
return formatters.postProcessor(formatters.generateNamespace(namespace, schemaToInterfaces(schema, options, formatters)));
};
The other, perhaps better, option is to change the line in the CLI that calls require to get the 'default' export.
https://github.com/avantcredit/gql2ts/blob/master/packages/util/package.json#L31
This is causing a second version of graphql
to be installed in my project. (which doesn't work)
Allow an option to generate interfaces from a given query and schema
Seems like currently there are no docblocks generated for comments on fields, only for types
from this comment by @BinaryMuse #76 (comment):
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; }
This is what I get as a type for a query:
interface IQuery {
__typename: string;
someQuery: ISomeType | null;
}
Why? Shouldn't I expect this package to generate types for the arguments as well?
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;
.
Hi there, great work on this project so far! I ran across an issue in typeToInterface in fromSchema/index.ts. If a GraphQLUnionType is used in the schema, it throws TypeError: type.getFields is not a function
at Line 173 in :
let isInput: boolean = type instanceof GraphQLInputObjectType;
const f1: GraphQLInputFieldMap | GraphQLFieldMap<any, any> =
(type instanceof GraphQLInputObjectType) ? type.getFields() : ((type as GraphQLObjectType).getFields)();
let f: Array<GraphQLField<any, any> | GraphQLInputField> = Object.keys(f1).map(k => f1[k]);
When reading from stdin and writing to stdout the usage information of gql2ts
is prepended to the output. This does not happen when an output file is specified.
I'm working on a new language package, and want to override the types in the DEFAULT_TYPE_MAP, but no matter what I do, everything seems to use the default type map.
e.g.
export const typeMap: ITypeMap = { ...DEFAULT_TYPE_MAP, DateTime: 'DateTime' };
seems to have no impact. Overriding the DEFAULT_TYPE_MAP also doesn't seem to do anything:
export const DEFAULT_TYPE_MAP: IDefaultTypeMap = { ID: 'string', String: 'string', Boolean: 'bool', Float: 'float', Int: 'int', __DEFAULT: 'object' };
This still maps default to 'any' instead of 'object',
and all ints and floats to number. Looking at the code for from-schema, it looks like it imports the TS defaults, but also tries to inject the options from the new package.
Right now I'm forcing the issue by utilizing a post-processor that does a LOT of replaces.
I'd like to be able to import generated interfaces using ES6 syntax rather than triple-slash references.
This could be as simple as emitting
declare module 'GQL' { ... }
instead of
declare namespace GQL { ... }
In my schema I have something like
userIds: [ID!]
Which should convert to
userIds?: Array<ID> | null;
But it's converted to
userIds: Array<ID>;
Now that #192 is merged in, it would be awesome if you could make a new release. 🚀
null
and undefined
typesAfter updating to the latest version, I get this diff on my schema:
@@ -24,7 +24,7 @@ declare namespace GQL {
interface IQuery {
__typename: "Query";
root: IRoot;
- node: INode | null;
+ node: Node | null;
}
/*
@@ -90,6 +90,11 @@ declare namespace GQL {
/*
description: null
*/
+ type Node = IRepository | ICommit;
+
+ /*
+ description: null
+ */
interface INode {
__typename: "Node";
id: string;
@@ -320,23 +325,21 @@ declare namespace GQL {
description: null
*/
interface ISearchQuery {
- __typename: "SearchQuery";
pattern: string;
isRegExp: boolean;
isWordMatch: boolean;
isCaseSensitive: boolean;
fileMatchLimit: number;
- includePattern: string | null;
- excludePattern: string | null;
+ includePattern?: string | null;
+ excludePattern?: string | null;
}
/*
description: null
*/
interface IRepositoryRevision {
- __typename: "RepositoryRevision";
repo: string;
- rev: string | null;
+ rev?: string | null;
}
In a GraphQL query we can alias fields, which I’m wondering how one should deal with in TypeScript and a tool like this.
Seeing as it really is related to a query, rather than just the schema, I’m thinking that this should be dealt with by implementing something like #30.
The only other issue, in our case, is that we use Relay, so it would be ideal if we could somehow plug into the compiler, extract the query from the Relay fragments, generate the type interfaces, and then actually compile the rest of the file. (This may well be beyond the scope of this tool.)
(Filing this here for visibility, but my colleague @orta said he was going to take a look at this.)
I wanted to have flow as a target language but saw not tests or examples hence my assumption that its a work in progress. If its incomplete it would be good to update the readme to that effect.
Hi,
Would be super useful if each field of a record accepted either the type itself or a promise to the type. e.g. given a schema:
type Author {
id: Int!
firstName: String
lastName: String
posts: [Post] # the list of Posts by this author
}
type Post {
id: Int!
title: String
author: Author
votes: Int
}
The generated code currently looks like this:
interface IPost {
__typename: string;
id: number;
title: string | null;
author: IAuthor | null;
votes: number | null;
}
When using Apollo it would be nice on the server side for the generated IPost
interface to look more like:
interface IPost {
...
author: Author | null | Promise<Author | null>;
...
}
What would you think of such a change?
Here's a snippet from one of my flow exports
flowtypes.js
export type AnalyticsSetResult = IConversationMetricResult | IBotUpdateMetricResult;
export interface IAnalyticsSetResult = {
__typename: "AnalyticsSetResult";
id: string;
sets: Array<AnalyticsSet> | null;
}
schema.graphql
interface AnalyticsSetResult {
id: ID!
sets: [AnalyticsSet]
}
type ConversationMetricResult implements AnalyticsSetResult {
id: ID!
sets: [ConversationMetricSet]
}
I can sort of see why this decision was made - you wanted a concrete type with the name of the interface that is a union of all the types implementing that interface. So to get around that you moved all graphql types to be interfaces and only generate types for these unions. It seems unnecessary and its confusing to have different names between code types and graphql types.
Command-line: gql2ts schema.json -o src/app/interfaces.ts
declare namespace GQL {
interface IGraphQLResponseRoot {
data?: IQuery | IMutation;
errors?: Array<IGraphQLResponseError>;
}
}
The types and namespace is in the generated interfaces.ts file, but how can I use those interfaces, since they are not exported.
I know there have been several issues on this in the past (I've been through this before!) and all had been fixed. But it appears that Graphql 0.13 may break this again
I'm on version 1.4.1
of gql2ts and all was working. But upon upgrading relay-compiler
to version 1.5
everything started breaking. On further inspection, relay-compiler
1.4.0requires graphql
^0.11.3and version
1.5ups that to version
^0.13.0`.
If I downgrade relay to 1.4, all works again.
It would be awesome if GraphQL enums were converted to const enum
instead of union types. That way we could write GitHubGQL.IStatusStateEnum.SUCCESS
as a value instead of 'SUCCESS'
the typescriptPrettify module depends on the typescript.formatter.RulesProvider
it seems this was removed in a recent release of TypeScript.
Since the CLI pulls this module in, I'm not able to use it on [email protected]
language-typescript
declares its typescript depdency as ^2.2.2
which means it will automatically install 2.7.
I think the version needs to be locked to 2.2.2 (ie, no ^
in the dependency), or the prettifier should be updated to 2.7.
Alternatively, lock typescript to the last working version which seems to be 2.6
We have an enum with no fields, or inputFields in the schema.json
{
"kind": "UNION",
"name": "PartnerTypes",
"description": null,
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": null,
"possibleTypes": [
{
"kind": "OBJECT",
"name": "Partner",
"ofType": null
},
{
"kind": "OBJECT",
"name": "ExternalPartner",
"ofType": null
}
]
},
This currently crashes the process. Will send a PR.
There should be an option to auto generate a .js
file which contains the original query.
Technically it would create two files — .d.ts
and a .js
file. This will make life much easier because then u can simply import the file. The corresponding .d.ts
file will also have to contain an additional typings as follow —
export const GQL: string`
I'd be happy to send a PR !
My graphql schema types are in individual files and I don't export them out as strings.
I had some logic in my code that would merge them together and then feed them into the generateNamespace function of the fromSchema package to generate typescript types.
I have gotten to a point where I want to do this using the CLI.
The CLI API currently supports only taking in one schema type file for generating types which wouldn't be ideal for my use case where I have multiple types files.
I have added a glob feature to the lib for the CLI that merges different .gql types into one.
I can submit a pull request of this feature for review if this something you wouldn't mind supporting.
Per #131, typescript has changed their formatting api.
Moving forward, we should find a better option.
Some options of the top of my head:
Certainly open to other suggestions
Unfortunately the use of real enums means the typings cannot be generated anymore referencing the enums will fail at runtime with Uncaught ReferenceError: GQL is not defined
. I think using const enum
s might work, but I think the better solution here is to generate a proper .ts module. That however gets hairy to combine with external modules, as the current file doesn't export anything and it can't be imported.
I would propose to add a new flag --module
that makes gql2ts output an external module (exporting the types) instead of a declare namespace
.
A schema can have two types that only vary by case. E.g.
type fooBar {
id: Int
}
type FooBar {
id: Float
name: String
}
This is valid in graphql...but gql2ts seems to change the case, and not check for duplicates, resulting in an invalid schema.
See pull request #10. And just close this here ;)
I have a schema part like:
{
"name": "rowId",
"description": "Checks for equality with the object’s `rowId` field.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
},
and it is converted to: rowId?: any;
Is it easy to convert it to number instead?
Currently the code does not handle GraphQL Enums. I think the wanted generator is a string literal union.
For example, the following enum:
enum UserType {
ADMIN
SELLER
CUSTOMER
}
Will become:
export type UserType = 'ADMIN' | 'SELLER' | 'CUSTOMER';
@brettjurgens LMK if this a good option in you opinion.
Thanks ahead!
Hi, first thanks for this repo. It's awesome :D
A problem which I have is that field names may not used in different table more than once. Otherwise I get following warning:
Interface 'INode' cannot simultaneously extend types 'IQuery' and 'IMyTable'.
Named property 'title' of types 'IQuery' and 'IMyTable' are not identical.
In IQuery is a title function which gives me an MyTable row back but in MyTable itself is a field named title. So interface INode extends IQuery, IMyTable...
complains.
When doing a query like this
query {
search(text: "cat") {
... on Movie {
director
}
... on Book {
author
}
}
}
on a schema like this
union SearchResult = Movie | Book
type Query {
search(text: String!): [SearchResult]!
}
you need to do a type check in the TS code to find out what type the results are, like this
if (item.__typename === 'Movie') {
show(item.director)
} else {
show(item.author)
}
TypeScript supports this through discriminated unions if the schema in TS looks like this:
type SearchResult = Movie | Book
interface Movie {
__typename: 'Movie'
director: string
}
interface Book {
__typename: 'Book'
author: string
}
but it currently looks like this:
type SearchResult = Movie | Book
interface Movie {
__typename: string
director: string
}
interface Book {
__typename: string
author: string
}
how to generate ts typings without namespace wrapper? only export each interface
Would you be interested in supporting passing in a URL as opposed to only a filename?
That way, we could do something like:
gpl2ts http://example.com/graphql
and get our generated types.
https://www.npmjs.com/package/fetch-graphql-schema supports taking a URL and returning JSON, so most of the work is already done.
I'd be willing to do a PR if this was something you were interested in. For now, I can do it in 2 steps (use the mentioned package to create the file, and then pass that file as input), but it'd be a lot nicer in our workflow to not have to create the intermediate file.
So I'm using gql2ts to generate interfaces to statically typecheck my resolver functions. One issue is that those functions should not populate the __typename
field in the objects they return but then the returned objects fail to typecheck.
Would there be interest in adding an option to omit the __typename
fields or possibly include them in a subtype? If so, I'll write and send a PR.
so, instead of:
interface IUser {
__typename: string;
id: string;
email: string;
}
we'd have something like:
interface IUser_NoTypename {
id: string;
email: string;
}
interface IUser extends IUser_NoTypename {
__typename: string;
}
Getting this error when running gql2ts from an introspection query:
$ get-graphql-schema http://localhost:3080/.api/graphql --json | gql2ts -o gqlschema.d.ts
/Users/felix/.../node_modules/@gql2ts/from-schema/dist/index.js:123
const f1 = type.getFields();
^
TypeError: type.getFields is not a function
at typeToInterface (/Users/felix/.../node_modules/@gql2ts/from-schema/dist/index.js:123:25)
at typeArr.filter.filter.map.type (/Users/felix/.../node_modules/@gql2ts/from-schema/dist/index.js:159:26)
at Array.map (native)
at typesToInterfaces (/Users/felix/.../node_modules/@gql2ts/from-schema/dist/index.js:159:14)
at run (/Users/felix/.../node_modules/@gql2ts/from-schema/dist/index.js:165:12)
at Object.exports.schemaToInterfaces (/Users/felix/.../node_modules/@gql2ts/from-schema/dist/index.js:167:73)
at Object.exports.generateNamespace (/Users/felix/.../node_modules/@gql2ts/from-schema/dist/index.js:170:85)
at run (/Users/felix/.../node_modules/gql2ts/dist/index.js:39:35)
at Socket.process.stdin.on (/Users/felix/.../node_modules/gql2ts/dist/index.js:55:35)
at emitNone (events.js:91:20)
This started happening after I added a union type. Could that be related?
When running gql2ts
as part of certain task runners STDIN will not be a TTY.
This causes this code here:
to read from STDIN even if a filename is provided, which ultimately will result in the hard-to-debug error
GraphQLError: Syntax Error GraphQL request (1:1) Unexpected <EOF>
because the STDIN is empty.
I would propose to always read from file when a file name is given.
Should we support transformations?
i.e. running an output through https://github.com/domchristie/humps to camelize?
use case: API returns underscored values, client wants them in camelcase
interfaces
, Language Y doesn't have Partial
, etc.)any
but should be { a: number; b: string }
Admin | Customer | Service
but we know that it can only ever be Admin
on the admin application, Customer
on the customer, etc.from-query should be able to figure out the __typename
of an object
To see what happens to your code in Node.js 10, Greenkeeper has created a branch with the following changes:
.travis.yml
package.json
files, so that was left aloneIf you’re interested in upgrading this repo to Node.js 10, you can open a PR with these changes. Please note that this issue is just intended as a friendly reminder and the PR as a possible starting point for getting your code running on Node.js 10.
Greenkeeper has checked the engines
key in any package.json
file, the .nvmrc
file, and the .travis.yml
file, if present.
engines
was only updated if it defined a single version, not a range..nvmrc
was updated to Node.js 10.travis.yml
was only changed if there was a root-level node_js
that didn’t already include Node.js 10, such as node
or lts/*
. In this case, the new version was appended to the list. We didn’t touch job or matrix configurations because these tend to be quite specific and complex, and it’s difficult to infer what the intentions were.For many simpler .travis.yml
configurations, this PR should suffice as-is, but depending on what you’re doing it may require additional work or may not be applicable at all. We’re also aware that you may have good reasons to not update to Node.js 10, which is why this was sent as an issue and not a pull request. Feel free to delete it without comment, I’m a humble robot and won’t feel rejected 🤖
There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.
Your Greenkeeper Bot 🌴
That would allow to catch usage of deprecated fields with the deprecation TSLint rule https://palantir.github.io/tslint/rules/deprecation/
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.