resolve `makeFragmentData` type problem by creating unmask Fragment utility type
You can try the useFragment function and UnmaskFragment type with graphql-code-generator-unmask-fragment package until it will be merged.
Description
Related https://github.com/dotansimha/graphql-code-generator/issues/9702 also related to https://github.com/dotansimha/graphql-code-generator/pull/9380
makeFragmentData can't make a mock data with a fragment includes multiple fragments (see #9702 ).
I created UnmaskFragment and apply it to the first argument of the function to solve the problem.
Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
- [x] Breaking change (fix or feature that would cause existing functionality to not work as expected)
This might be a breaking change if you are using nested makeFragmentData to create a mock data for a nested fragment as like follows:
makeFragmentData({
bar: makeFragmentData({ ... }, BarFragment)
}, SomeFragment);
Screenshots/Sandbox (if appropriate/relevant):
sandbox of testing the UnmaskingFragment utility type
How Has This Been Tested?
- check the failing code with the current
makeFragmentDataat a sandbox 1.1. clone https://github.com/tnyo43/graphql-code-generator-issue-fragment-conflict and move to the root of it. 1.2. runpnpm run install & pnpm run generate. 1.3. open "./src/User.tsx" and check themockDatais not able to be typed without any type assertion.- this is because
User_UserFragmentincludes multiple fragment.
- this is because
- prepare this repository
2.1 clone this repository and checkout to this branch (tnyo43:update-make-fragment-data-args-type)
2.2 run
yarn install & yarn build - apply the change to the sandbox (see https://github.com/tnyo43/graphql-code-generator-issue-fragment-conflict/pull/1)
3.1. move to sandbox and update the dependency of "@graphql-codegen/client-preset" to refer to the local change (ex.
"@graphql-codegen/client-preset": "file:../graphql-code-generator/packages/presets/client"). 3.2. runpnpm run install & pnpm run generate. 3.3. open "./src/User.tsx" and updatemockDataas like follows. You will see that we don't need any extra type assertion for making mock data!
const mockData = makeFragmentData(
{
id: "user_1",
username: "aaa",
avatarUrl: "aaa",
email: "[email protected]",
},
User_UserFragment
);
Test Environment:
- OS: macOS 14.0 (23A344)
-
@graphql-codegen/cli: 4.0.1 -
@graphql-typed-document-node/core: 3.2.0, -
typescript: 5.2.2 - NodeJS: 18.16.0
Checklist:
- [x] I have followed the CONTRIBUTING doc and the style guidelines of this project
- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [N/A] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my feature works
- [x] New and existing unit tests pass locally with my changes
- [x] Any dependent changes have been merged and published in downstream modules
Further comments
If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc...
🦋 Changeset detected
Latest commit: 015cefe3c2c5497ac6c3c7cbc905006de7dcb737
The changes in this PR will be included in the next version bump.
This PR includes changesets to release 1 package
| Name | Type |
|---|---|
| @graphql-codegen/client-preset | Minor |
Not sure what this means? Click here to learn what changesets are.
Click here if you're a maintainer who wants to add another changeset to this PR
Can anyone help me to fix ci problem? https://github.com/dotansimha/graphql-code-generator/actions/runs/6524719251/job/17716636556?pr=9708
@saihaj Could you please provide some advice on how to approach this? As I mentioned in the description, I know it is kind a breaking change so we may need to discuss a lot to merge.
Hello,
I wanted to both bump this pr so hopefully @beerose @n1ru4l can take a look.
While this PR or another related get's merged I created this unmasked.ts
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;
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>>;
Example query:
const query = graphql(`
query GetFolderStructure($sitename: String!) {
getSite(sitename: $sitename) {
id
name
folders {
...FolderFragment
children {
...FolderFragment
children {
...FolderFragment
children {
...FolderFragment
children {
...FolderFragment
}
}
}
}
}
}
}
`);
And used it like:
const result = await execute(query, { sitename });
type UnmaskedResult = UnmaskFragment<NonNullable<typeof result>>;
But it does not work, perhaps this unmasked.ts file is incorrect or I'm using it wrong, I tried searching inside the changed files but there are way too many and not sure which one to use.