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

resolve makeFragmentData type problem by creating unmask Fragment utility type #9708

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
5 changes: 5 additions & 0 deletions .changeset/early-toes-stare.md
@@ -0,0 +1,5 @@
---
'@graphql-codegen/client-preset': minor
---

Update `makeFragmentData` type to solve a problem when make mock data for fragments with multiple fragments inside
44 changes: 40 additions & 4 deletions dev-test/gql-tag-operations-masking/gql/fragment-masking.ts
@@ -1,8 +1,13 @@
/* eslint-disable */
import { ResultOf, DocumentTypeDecoration, TypedDocumentNode } from '@graphql-typed-document-node/core';
import { DocumentTypeDecoration, TypedDocumentNode } from '@graphql-typed-document-node/core';
import { FragmentDefinitionNode } from 'graphql';
import { Incremental } from './graphql.js';

type Primitive = string | number | boolean | bigint | symbol | null | undefined;
type ExcludePrimitive<T> = Exclude<T, Primitive>;
type ExtractPrimitive<T> = Exclude<T, Exclude<T, Primitive>>;
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;

export type FragmentType<TDocumentType extends DocumentTypeDecoration<any, any>> =
TDocumentType extends DocumentTypeDecoration<infer TType, any>
? [TType] extends [{ ' $fragmentName'?: infer TKey }]
Expand Down Expand Up @@ -43,11 +48,42 @@ export function useFragment<TType>(
return fragmentType as any;
}

export function makeFragmentData<F extends DocumentTypeDecoration<any, any>, FT extends ResultOf<F>>(
data: FT,
type UnionToIntersectGroupByTypeName<U, V = U> = [V] extends [{ __typename?: infer TypeName }]
? TypeName extends any
? UnionToIntersection<U extends { __typename?: TypeName } ? U : never>
: never
: never;

type UnionFieldToIntersection<T> = [T] extends [never]
? never
: [T] extends [Array<unknown>]
? Array<UnionFieldToIntersection<ExcludePrimitive<T[number]>> | ExtractPrimitive<T[number]>>
: UnionToIntersectGroupByTypeName<T> extends infer V
? {
[Key in keyof V]: UnionFieldToIntersection<ExcludePrimitive<V[Key]>> | ExtractPrimitive<V[Key]>;
}
: never;

type Flatten<F> = [F] extends [never]
? never
: F extends Array<unknown>
? Array<Flatten<ExcludePrimitive<F[number]>> | ExtractPrimitive<F[number]>>
: {
[Key in keyof Omit<F, ' $fragmentRefs' | ' $fragmentName'>]:
| Flatten<ExcludePrimitive<F[Key]>>
| ExtractPrimitive<F[Key]>;
} & (F extends { ' $fragmentRefs'?: { [K in string]: infer FRefs } }
? FRefs extends any
? Flatten<FRefs>
: never
: {});
export type UnmaskFragment<F> = UnionFieldToIntersection<Flatten<F>>;

export function makeFragmentData<F extends DocumentTypeDecoration<any, any>>(
data: UnmaskFragment<FragmentType<F>>,
_fragment: F
): FragmentType<F> {
return data as FragmentType<F>;
return data as any;
}
export function isFragmentReady<TQuery, TFrag>(
queryNode: DocumentTypeDecoration<TQuery, any>,
Expand Down
61 changes: 58 additions & 3 deletions dev-test/gql-tag-operations-masking/gql/gql.ts
Expand Up @@ -15,11 +15,24 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/
const documents = {
'\n fragment TweetFragment on Tweet {\n id\n body\n ...TweetAuthorFragment\n }\n':
types.TweetFragmentFragmentDoc,
'\n fragment TweetStatsFragment on Tweet {\n id\n Stats {\n views\n }\n }\n':
types.TweetStatsFragmentFragmentDoc,
'\n fragment TweetAuthorFragment on Tweet {\n id\n author {\n id\n username\n }\n }\n':
types.TweetAuthorFragmentFragmentDoc,
'\n fragment TweetsFragment on Query {\n Tweets {\n id\n ...TweetFragment\n }\n }\n':
'\n fragment TweetsFragment on Query {\n Tweets {\n id\n ...TweetFragment\n ...TweetStatsFragment\n }\n }\n':
types.TweetsFragmentFragmentDoc,
'\n query TweetAppQuery {\n ...TweetsFragment\n }\n': types.TweetAppQueryDocument,
'\n fragment UserFragment on User {\n id\n username\n }\n': types.UserFragmentFragmentDoc,
'\n fragment TweetWithUserFragment on Tweet {\n id\n body\n author {\n ...UserFragment\n }\n }\n':
types.TweetWithUserFragmentFragmentDoc,
'\n fragment UserWithNameFragment on User {\n id\n username\n first_name\n last_name\n }\n':
types.UserWithNameFragmentFragmentDoc,
'\n fragment TweetWithUserNameFragment on Tweet {\n id\n body\n author {\n ...UserFragment\n ...UserWithNameFragment\n }\n }\n':
types.TweetWithUserNameFragmentFragmentDoc,
'\n fragment UserWithNestedFollowersAndTweetsFragment on User {\n id\n ...UserFragment\n ...UserWithNameFragment\n full_name\n Followers {\n id\n ...UserFragment\n Followers {\n id\n ...UserFragment\n }\n Tweets {\n ...TweetWithUserFragment\n }\n }\n Tweets {\n id\n ...TweetWithUserFragment\n author {\n id\n Followers {\n id\n ...UserFragment\n }\n }\n }\n }\n':
types.UserWithNestedFollowersAndTweetsFragmentFragmentDoc,
'\n fragment QueryOfNotificationsFragment on Query {\n Notifications {\n id\n\n ... on Message {\n body\n from {\n id\n }\n }\n ... on Information {\n body\n priority\n }\n }\n }\n':
types.QueryOfNotificationsFragmentFragmentDoc,
};

/**
Expand All @@ -42,6 +55,12 @@ export function graphql(source: string): unknown;
export function graphql(
source: '\n fragment TweetFragment on Tweet {\n id\n body\n ...TweetAuthorFragment\n }\n'
): (typeof documents)['\n fragment TweetFragment on Tweet {\n id\n body\n ...TweetAuthorFragment\n }\n'];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(
source: '\n fragment TweetStatsFragment on Tweet {\n id\n Stats {\n views\n }\n }\n'
): (typeof documents)['\n fragment TweetStatsFragment on Tweet {\n id\n Stats {\n views\n }\n }\n'];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
Expand All @@ -52,14 +71,50 @@ export function graphql(
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(
source: '\n fragment TweetsFragment on Query {\n Tweets {\n id\n ...TweetFragment\n }\n }\n'
): (typeof documents)['\n fragment TweetsFragment on Query {\n Tweets {\n id\n ...TweetFragment\n }\n }\n'];
source: '\n fragment TweetsFragment on Query {\n Tweets {\n id\n ...TweetFragment\n ...TweetStatsFragment\n }\n }\n'
): (typeof documents)['\n fragment TweetsFragment on Query {\n Tweets {\n id\n ...TweetFragment\n ...TweetStatsFragment\n }\n }\n'];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(
source: '\n query TweetAppQuery {\n ...TweetsFragment\n }\n'
): (typeof documents)['\n query TweetAppQuery {\n ...TweetsFragment\n }\n'];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(
source: '\n fragment UserFragment on User {\n id\n username\n }\n'
): (typeof documents)['\n fragment UserFragment on User {\n id\n username\n }\n'];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(
source: '\n fragment TweetWithUserFragment on Tweet {\n id\n body\n author {\n ...UserFragment\n }\n }\n'
): (typeof documents)['\n fragment TweetWithUserFragment on Tweet {\n id\n body\n author {\n ...UserFragment\n }\n }\n'];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(
source: '\n fragment UserWithNameFragment on User {\n id\n username\n first_name\n last_name\n }\n'
): (typeof documents)['\n fragment UserWithNameFragment on User {\n id\n username\n first_name\n last_name\n }\n'];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(
source: '\n fragment TweetWithUserNameFragment on Tweet {\n id\n body\n author {\n ...UserFragment\n ...UserWithNameFragment\n }\n }\n'
): (typeof documents)['\n fragment TweetWithUserNameFragment on Tweet {\n id\n body\n author {\n ...UserFragment\n ...UserWithNameFragment\n }\n }\n'];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(
source: '\n fragment UserWithNestedFollowersAndTweetsFragment on User {\n id\n ...UserFragment\n ...UserWithNameFragment\n full_name\n Followers {\n id\n ...UserFragment\n Followers {\n id\n ...UserFragment\n }\n Tweets {\n ...TweetWithUserFragment\n }\n }\n Tweets {\n id\n ...TweetWithUserFragment\n author {\n id\n Followers {\n id\n ...UserFragment\n }\n }\n }\n }\n'
): (typeof documents)['\n fragment UserWithNestedFollowersAndTweetsFragment on User {\n id\n ...UserFragment\n ...UserWithNameFragment\n full_name\n Followers {\n id\n ...UserFragment\n Followers {\n id\n ...UserFragment\n }\n Tweets {\n ...TweetWithUserFragment\n }\n }\n Tweets {\n id\n ...TweetWithUserFragment\n author {\n id\n Followers {\n id\n ...UserFragment\n }\n }\n }\n }\n'];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(
source: '\n fragment QueryOfNotificationsFragment on Query {\n Notifications {\n id\n\n ... on Message {\n body\n from {\n id\n }\n }\n ... on Information {\n body\n priority\n }\n }\n }\n'
): (typeof documents)['\n fragment QueryOfNotificationsFragment on Query {\n Notifications {\n id\n\n ... on Message {\n body\n from {\n id\n }\n }\n ... on Information {\n body\n priority\n }\n }\n }\n'];

export function graphql(source: string) {
return (documents as any)[source] ?? {};
Expand Down