Description
Is your feature request related to a problem? Please describe.
I recently migrated a project to client-preset
(from gql-tag-operations-preset
) and would like to turn on Fragment Masking for it. Unfortunately, because of existing fragments that are not using Fragment Masking, we already have a thousand errors to handle to be able to.
When you enable Fragment Masking, all fragments, and operations are automatically masked, even though not all fragments or operations use Fragment Masking.
I would like the ability to unmask fragments recursively to support an incremental adoption of Fragment Masking.
Also related to:
- Generate both masked fragment types and full query types. #9308
- Share fragment between query and mutation without masking #9325
Nested Fragment: <UserAvatar>
export const UserAvatarUserFragment = gql(`
fragment UserAvatarUser on User {
avatarUrl
}
`);
type UserAvatarProps = {
user: ResultOf<typeof UserAvatarUserFragment>;
};
function UserAvatar(props: UserAvatarProps) {
return <img src={props.user.avatarUrl} />;
}
Fragment: <UserCard>
export const UserCardUserFragment = gql(`
fragment UserCardUser on User {
id
name
...UserAvatarUserFragment
}
`);
type UserCardProps = {
user: ResultOf<typeof UserCardUserFragment>;
};
function UserCard(props: UserCardProps) {
return (
<>
<UserAvatar user={props.user} />
{/* ^ Property 'avatarUrl' is missing in type */}
{/* ^ Type '"UserCardUserFragment"' is not assignable to type '"UserAvatarUserFragment"' */}
<div>ID: {props.user.id}</div>
<div>Name: {props.user.name}</div>
</>
);
}
Query: <ExampleComponent>
export const ExampleComponentQuery = gql(`
query ExampleComponent($id: String!) {
user(id: $id) {
...UserCardUser
}
}
`);
type ExampleComponentProps = {
id: VariablesOf<typeof ExampleComponentQuery>['id'];
};
export function ExampleComponent(props: ExampleComponentProps) {
const { loading, data } = useQuery(ExampleComponentQuery, { variables: { id: props.id } });
if (loading) {
return <>loading...</>;
}
if (!data?.user) {
return <>no user</>;
}
return <UserCard user={data.user} />;
}
Describe the solution you'd like
We could expose the following utility type: UnmaskResultOf<TypedDocumentNode>
.
This would check if whether we're inside an operation or directly within a fragment and then recursively flatten fragments by resolving $fragmentRefs
and merging them to the root fragment.
type UserCardProps = {
- user: ResultOf<typeof UserCardUserFragment>;
- // ^? (property) user: { __typename: "User"; id: string; name: string; ' $fragmentRefs': { ... }}
+ user: UnmaskResultOf<typeof UserCardUserFragment>;
+ // ^? (property) user: { __typename: "User"; id: string; name: string; avatarUrl: string }
};
const testUnmaskResultOf: UnmaskResultOf<ExampleComponentQuery> = {
__typename: 'Query',
user: {
__typename: 'User',
id: 'some-id',
name: 'some-name',
avatarUrl: 'some-avatar-url',
},
};
See the implementation on TypeScript Playground: Link.
Warning: Pull request with the full implementation will follow.
Describe alternatives you've considered
No response
Is your feature request related to a problem? Please describe.
No response