-
Notifications
You must be signed in to change notification settings - Fork 26
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
feat: emit TypedDocumentNode
alias in generated code
#230
Conversation
This change adds an exported type alias from each generated module based on the `TypedDocumentNode` type from `@graphql-typed-document-node/core`. Apollo Client and Villus are able to consume this type and propagate result data and variables types correctly. It looks like support for this type is also [coming][1] to Apollo Angular and to GraphQL.js itself. For example, import { gql, useQuery } from '@apollo/client'; const query = gql` query GitHubQuery($first: Int!) { viewer { repositories(first: $first) { nodes { id description } } } } ` as import("./__generated__/git-hub-query.ts").GithubQueryDocument; // ^ // type assertion is co-located with query export default () => { const { data } = useQuery(query, { variables: { first: 100 } }); // ^ ^ // inferred type is `GithubQuery` | // | // inferred type is `GithubQueryVariables` /* ... */ }; As a result types can be hooked up with one type assertion that is applied directly to the query expression. [1]: https://github.com/dotansimha/graphql-typed-document-node#upcoming-built-in-support
Codecov Report
@@ Coverage Diff @@
## master #230 +/- ##
==========================================
+ Coverage 91.09% 91.18% +0.08%
==========================================
Files 47 47
Lines 3696 3732 +36
Branches 451 457 +6
==========================================
+ Hits 3367 3403 +36
Misses 329 329
|
Hi. @hallettj thanks for PR. I see your comment, #117 (comment) . I've not seen import { DocumentNode } from 'graphql';
export interface TypedDocumentNode<Result = {
[key: string]: any;
}, Variables = {
[key: string]: any;
}> extends DocumentNode {
__resultType?: Result;
__variablesType?: Variables;
}
I understand this feature may be useful for AC users and I like your ESLint plugin fixer. But I can't merge this to my type generator because:
I think my type-generator should expose some functions to customize. Then we can provide this /* tsconfig */
{
plugins:[
{
name: "ts-graphql-plugin",
schema: "schema.gql",
typegen: {
addons: ["./typed-document-node"]
}
}
} /* typed-document-node.ts */
export default function addon(source: ts.SourceFile, generationContext) {
const importStatement = ts.factory.createImport(...);
const exportTypedDocumentNodeStatement = ts.factoryCreateExport(...) // create with generationContext
return ts.factory.updateSourceFileNode(source, ...); // push the statements
} |
Thanks for the quick reply! I'll try to address your questions, and to make my case. I have more detailed explanations below, but briefly:
I respect your preference to avoid adding a special feature to support one library. But I don't think that's the case with this PR. I disagree that the solution is "hacky". Well ok, it is a little hacky. But phantom types are a proven pattern with legitimate uses (again, more details below). It's not an idea that is specific to The
|
I'm sorry, I dumped a lot of text on you yesterday. After writing all that I see how the typed document node idea is rather experimental. I have one more idea, which is that the typegen command could check the version of graphql that is installed, and emit a |
export interface TypedQueryDocumentNode<
TResponseData = Record<string, any>,
TRequestVariables = Record<string, any>
> extends DocumentNode {
readonly definitions: ReadonlyArray<ExecutableDefinitionNode>;
// FIXME: remove once TS implements proper way to enforce nominal typing
/**
* This type is used to ensure that the variables you pass in to the query are assignable to Variables
* and that the Result is assignable to whatever you pass your result to. The method is never actually
* implemented, but the type is valid because we list it as optional
*/
__ensureTypesOfVariablesAndResultMatching?: (
variables: TRequestVariables,
) => TResponseData;
} It seems also experimental because the above FIXME comment. (And I think there're a little differences of purpose between nominal typings and result/variables type inference 🤔 ) But I like graphql-js's If graphql-js v16 be released, it may be nice to generate like the following: /* __generated/my-query.ts */
import { TypedQueryDocumentNode } from 'graphql'
export type MyQueryResult = ...
export type MyQueryVariables = ...
export type MyQueryDocumentNode = TypedQueryDocumentNode<MyQueryResult, MyQueryVariables>; In any case, we should to provide a way to opt-in ( or opt-out ) outputting |
I don't think that FIXME serves much purpose. As you say nominal types are a different idea. It just happens that both nominal types and phantom types are implemented with hypothetical properties in TypeScript. And I don't think either pattern is going to change because it would mean a substantial change to TypeScript's philosophy of structural typing. If the internal details of the type do change in the future it shouldn't make a difference for libraries that use it. The public API is just this part: interface TypedQueryDocumentNode<
TResponseData = Record<string, any>,
TRequestVariables = Record<string, any>
> extends DocumentNode I'll put in an opt-in configuration option, and switch to the |
@hallettj Thanks for refactoring and writing docs ! I'm writing prototype of functions to hook the type generator #232 . For add-on detail, see https://github.com/Quramy/ts-graphql-plugin/blob/typegen-addon/project-fixtures/typegen-addon-prj/addon.ts And I intend to implement your I plan users can make this feature enabled via the following option: "plugins": [
{
"name": "ts-graphql-plugin",
"tag": "gql",
"schema": "schema.graphql",
"typegen": {
"addons": ["ts-graphql-plugin/addons/typed-query-node"]
}
}
] ( Of course, after graphql-js v16 release, I'll change this How about it and can you wait a little until #232 is merged ? |
Cool, that works for me. Thank you for all of your time and effort! |
@hallettj . |
That's awesome - thanks so much!
…On Sat, Nov 21, 2020, 10:41 Yosuke Kurami ***@***.***> wrote:
@hallettj <https://github.com/hallettj> .
I implemented a typegen extension to use TypedQueryDocumentNode and
published it as v2.1.0. See
https://github.com/Quramy/ts-graphql-plugin#typed-query-document .
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#230 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAACLFXPTO3VSTQFT4LWDCLSQ7NSFANCNFSM4TVJXM6Q>
.
|
This change adds an exported type alias from each generated module based on the
TypedDocumentNode
type from@graphql-typed-document-node/core
. Apollo Client and Villus are able to consume this type and propagate result data and variables types correctly. It looks like support for this type is also coming to Apollo Angular and to GraphQL.js itself. For example:As a result types can be hooked up with one type assertion that is applied directly to the query expression. If this PR is merged I'd like to update @originate/eslint-plugin-ts-graphql to apply that type assertion automatically when running
eslint --fix
. I think this pattern is helpful because it allows generated types to be hooked up automatically, and in cases where a query is used in multiple call sites setting up types once on the query expression is more robust than setting up types at every call site.With the change generated files look like this:
The change adds a dependency on
@graphql-typed-document-node/core
. I know that your contributing guidelines say not to add dependencies - but this is a teeny tiny dependency that I think adds significant value.It is possible to avoid the new dependency by writing the type like this:
That interface is compatible with the current version of
TypedDocumentNode
. But from what I see in this commitTypedDocumentNode
may change in an upcoming release so that it is no longer be compatible with the above interface. If you depend on and importTypedDocumentNode
then you don't have to worry about the change.By the way I was not able to run the e2e tests, so I don't know whether I've broken them.