diff --git a/.api-reports/api-report-cache.api.md b/.api-reports/api-report-cache.api.md index 908176a26cf..6c52897efbb 100644 --- a/.api-reports/api-report-cache.api.md +++ b/.api-reports/api-report-cache.api.md @@ -764,16 +764,18 @@ export function makeReference(id: string): Reference; // @public (undocumented) export function makeVar(value: T): ReactiveVar; -// Warning: (ae-forgotten-export) The symbol "RemoveMaskedMarker" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "DataMasking" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "RemoveMaskedMarker" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ContainsFragmentsRefs" needs to be exported by the entry point index.d.ts // // @public -type MaybeMasked = TData extends any ? true extends IsAny ? TData : TData extends { +type MaybeMasked = DataMasking extends { + mode: "unmask"; +} ? TData extends any ? true extends IsAny ? TData : TData extends { __masked?: true; -} ? Prettify> : DataMasking extends { - enabled: true; -} ? TData : true extends ContainsFragmentsRefs ? Unmasked : TData : never; +} ? Prettify> : true extends ContainsFragmentsRefs ? Unmasked : TData : never : DataMasking extends { + mode: "preserveTypes"; +} ? TData : TData; // @public (undocumented) export interface MergeInfo { diff --git a/.api-reports/api-report-core.api.md b/.api-reports/api-report-core.api.md index 81836428979..f1d74960f19 100644 --- a/.api-reports/api-report-core.api.md +++ b/.api-reports/api-report-core.api.md @@ -1412,11 +1412,13 @@ type MaybeAsync = T | PromiseLike; // Warning: (ae-forgotten-export) The symbol "ContainsFragmentsRefs" needs to be exported by the entry point index.d.ts // // @public -export type MaybeMasked = TData extends any ? true extends IsAny ? TData : TData extends { +export type MaybeMasked = DataMasking extends { + mode: "unmask"; +} ? TData extends any ? true extends IsAny ? TData : TData extends { __masked?: true; -} ? Prettify> : DataMasking extends { - enabled: true; -} ? TData : true extends ContainsFragmentsRefs ? Unmasked : TData : never; +} ? Prettify> : true extends ContainsFragmentsRefs ? Unmasked : TData : never : DataMasking extends { + mode: "preserveTypes"; +} ? TData : TData; // @public (undocumented) export interface MergeInfo { diff --git a/.api-reports/api-report-masking.api.md b/.api-reports/api-report-masking.api.md index fffd2efb2e1..04786e31bbd 100644 --- a/.api-reports/api-report-masking.api.md +++ b/.api-reports/api-report-masking.api.md @@ -440,11 +440,13 @@ export function maskOperation(data: TData, document: DocumentNo // Warning: (ae-forgotten-export) The symbol "ContainsFragmentsRefs" needs to be exported by the entry point index.d.ts // // @public -export type MaybeMasked = TData extends any ? true extends IsAny ? TData : TData extends { +export type MaybeMasked = DataMasking extends { + mode: "unmask"; +} ? TData extends any ? true extends IsAny ? TData : TData extends { __masked?: true; -} ? Prettify> : DataMasking extends { - enabled: true; -} ? TData : true extends ContainsFragmentsRefs ? Unmasked : TData : never; +} ? Prettify> : true extends ContainsFragmentsRefs ? Unmasked : TData : never : DataMasking extends { + mode: "preserveTypes"; +} ? TData : TData; // Warning: (ae-forgotten-export) The symbol "CombineIntersection" needs to be exported by the entry point index.d.ts // diff --git a/.api-reports/api-report-react.api.md b/.api-reports/api-report-react.api.md index 16e002d9106..904d5977115 100644 --- a/.api-reports/api-report-react.api.md +++ b/.api-reports/api-report-react.api.md @@ -1153,16 +1153,18 @@ interface MaskOperationOptions { // @public (undocumented) type MaybeAsync = T | PromiseLike; -// Warning: (ae-forgotten-export) The symbol "RemoveMaskedMarker" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "DataMasking" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "RemoveMaskedMarker" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ContainsFragmentsRefs" needs to be exported by the entry point index.d.ts // // @public -type MaybeMasked = TData extends any ? true extends IsAny ? TData : TData extends { +type MaybeMasked = DataMasking extends { + mode: "unmask"; +} ? TData extends any ? true extends IsAny ? TData : TData extends { __masked?: true; -} ? Prettify> : DataMasking extends { - enabled: true; -} ? TData : true extends ContainsFragmentsRefs ? Unmasked : TData : never; +} ? Prettify> : true extends ContainsFragmentsRefs ? Unmasked : TData : never : DataMasking extends { + mode: "preserveTypes"; +} ? TData : TData; // Warning: (ae-forgotten-export) The symbol "CombineIntersection" needs to be exported by the entry point index.d.ts // @@ -2411,7 +2413,7 @@ export interface UseReadQueryResult { export function useSubscription(subscription: DocumentNode | TypedDocumentNode, options?: SubscriptionHookOptions, NoInfer_2>): { restart: () => void; loading: boolean; - data?: MaybeMasked | undefined; + data?: TData | undefined; error?: ApolloError; variables?: TVariables | undefined; }; diff --git a/.api-reports/api-report-react_components.api.md b/.api-reports/api-report-react_components.api.md index 051470cb154..1238640825e 100644 --- a/.api-reports/api-report-react_components.api.md +++ b/.api-reports/api-report-react_components.api.md @@ -1016,16 +1016,18 @@ interface MaskOperationOptions { // @public (undocumented) type MaybeAsync = T | PromiseLike; -// Warning: (ae-forgotten-export) The symbol "RemoveMaskedMarker" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "DataMasking" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "RemoveMaskedMarker" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ContainsFragmentsRefs" needs to be exported by the entry point index.d.ts // // @public -type MaybeMasked = TData extends any ? true extends IsAny ? TData : TData extends { +type MaybeMasked = DataMasking extends { + mode: "unmask"; +} ? TData extends any ? true extends IsAny ? TData : TData extends { __masked?: true; -} ? Prettify> : DataMasking extends { - enabled: true; -} ? TData : true extends ContainsFragmentsRefs ? Unmasked : TData : never; +} ? Prettify> : true extends ContainsFragmentsRefs ? Unmasked : TData : never : DataMasking extends { + mode: "preserveTypes"; +} ? TData : TData; // Warning: (ae-forgotten-export) The symbol "CombineIntersection" needs to be exported by the entry point index.d.ts // diff --git a/.api-reports/api-report-react_context.api.md b/.api-reports/api-report-react_context.api.md index 703cfb4825d..3fdc6fa3414 100644 --- a/.api-reports/api-report-react_context.api.md +++ b/.api-reports/api-report-react_context.api.md @@ -1013,16 +1013,18 @@ interface MaskOperationOptions { // @public (undocumented) type MaybeAsync = T | PromiseLike; -// Warning: (ae-forgotten-export) The symbol "RemoveMaskedMarker" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "DataMasking" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "RemoveMaskedMarker" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ContainsFragmentsRefs" needs to be exported by the entry point index.d.ts // // @public -type MaybeMasked = TData extends any ? true extends IsAny ? TData : TData extends { +type MaybeMasked = DataMasking extends { + mode: "unmask"; +} ? TData extends any ? true extends IsAny ? TData : TData extends { __masked?: true; -} ? Prettify> : DataMasking extends { - enabled: true; -} ? TData : true extends ContainsFragmentsRefs ? Unmasked : TData : never; +} ? Prettify> : true extends ContainsFragmentsRefs ? Unmasked : TData : never : DataMasking extends { + mode: "preserveTypes"; +} ? TData : TData; // Warning: (ae-forgotten-export) The symbol "CombineIntersection" needs to be exported by the entry point index.d.ts // diff --git a/.api-reports/api-report-react_hoc.api.md b/.api-reports/api-report-react_hoc.api.md index 3f867350519..6fe263dd146 100644 --- a/.api-reports/api-report-react_hoc.api.md +++ b/.api-reports/api-report-react_hoc.api.md @@ -1020,16 +1020,18 @@ interface MaskOperationOptions { // @public (undocumented) type MaybeAsync = T | PromiseLike; -// Warning: (ae-forgotten-export) The symbol "RemoveMaskedMarker" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "DataMasking" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "RemoveMaskedMarker" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ContainsFragmentsRefs" needs to be exported by the entry point index.d.ts // // @public -type MaybeMasked = TData extends any ? true extends IsAny ? TData : TData extends { +type MaybeMasked = DataMasking extends { + mode: "unmask"; +} ? TData extends any ? true extends IsAny ? TData : TData extends { __masked?: true; -} ? Prettify> : DataMasking extends { - enabled: true; -} ? TData : true extends ContainsFragmentsRefs ? Unmasked : TData : never; +} ? Prettify> : true extends ContainsFragmentsRefs ? Unmasked : TData : never : DataMasking extends { + mode: "preserveTypes"; +} ? TData : TData; // Warning: (ae-forgotten-export) The symbol "CombineIntersection" needs to be exported by the entry point index.d.ts // diff --git a/.api-reports/api-report-react_hooks.api.md b/.api-reports/api-report-react_hooks.api.md index b252b2a672f..1c5b8fb2058 100644 --- a/.api-reports/api-report-react_hooks.api.md +++ b/.api-reports/api-report-react_hooks.api.md @@ -1102,16 +1102,18 @@ interface MaskOperationOptions { // @public (undocumented) type MaybeAsync = T | PromiseLike; -// Warning: (ae-forgotten-export) The symbol "RemoveMaskedMarker" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "DataMasking" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "RemoveMaskedMarker" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ContainsFragmentsRefs" needs to be exported by the entry point index.d.ts // // @public -type MaybeMasked = TData extends any ? true extends IsAny ? TData : TData extends { +type MaybeMasked = DataMasking extends { + mode: "unmask"; +} ? TData extends any ? true extends IsAny ? TData : TData extends { __masked?: true; -} ? Prettify> : DataMasking extends { - enabled: true; -} ? TData : true extends ContainsFragmentsRefs ? Unmasked : TData : never; +} ? Prettify> : true extends ContainsFragmentsRefs ? Unmasked : TData : never : DataMasking extends { + mode: "preserveTypes"; +} ? TData : TData; // Warning: (ae-forgotten-export) The symbol "CombineIntersection" needs to be exported by the entry point index.d.ts // @@ -2244,7 +2246,7 @@ export interface UseReadQueryResult { export function useSubscription(subscription: DocumentNode | TypedDocumentNode, options?: SubscriptionHookOptions, NoInfer_2>): { restart: () => void; loading: boolean; - data?: MaybeMasked | undefined; + data?: TData | undefined; error?: ApolloError; variables?: TVariables | undefined; }; diff --git a/.api-reports/api-report-react_internal.api.md b/.api-reports/api-report-react_internal.api.md index 3e43a1dafbb..92a7a3747e6 100644 --- a/.api-reports/api-report-react_internal.api.md +++ b/.api-reports/api-report-react_internal.api.md @@ -956,7 +956,7 @@ export class InternalQueryReference { // Warning: (ae-forgotten-export) The symbol "FetchMoreOptions" needs to be exported by the entry point index.d.ts // // (undocumented) - fetchMore(options: FetchMoreOptions): Promise>>; + fetchMore(options: FetchMoreOptions): Promise>; // (undocumented) readonly key: QueryKey; // Warning: (ae-forgotten-export) The symbol "Listener" needs to be exported by the entry point index.d.ts @@ -968,7 +968,7 @@ export class InternalQueryReference { // (undocumented) promise: QueryRefPromise; // (undocumented) - refetch(variables: OperationVariables | undefined): Promise>>; + refetch(variables: OperationVariables | undefined): Promise>; // (undocumented) reinitialize(): void; // (undocumented) @@ -1112,16 +1112,18 @@ interface MaskOperationOptions { // @public (undocumented) type MaybeAsync = T | PromiseLike; -// Warning: (ae-forgotten-export) The symbol "RemoveMaskedMarker" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "DataMasking" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "RemoveMaskedMarker" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ContainsFragmentsRefs" needs to be exported by the entry point index.d.ts // // @public -type MaybeMasked = TData extends any ? true extends IsAny ? TData : TData extends { +type MaybeMasked = DataMasking extends { + mode: "unmask"; +} ? TData extends any ? true extends IsAny ? TData : TData extends { __masked?: true; -} ? Prettify> : DataMasking extends { - enabled: true; -} ? TData : true extends ContainsFragmentsRefs ? Unmasked : TData : never; +} ? Prettify> : true extends ContainsFragmentsRefs ? Unmasked : TData : never : DataMasking extends { + mode: "preserveTypes"; +} ? TData : TData; // Warning: (ae-forgotten-export) The symbol "CombineIntersection" needs to be exported by the entry point index.d.ts // diff --git a/.api-reports/api-report-react_ssr.api.md b/.api-reports/api-report-react_ssr.api.md index d0560e817dd..83958f92452 100644 --- a/.api-reports/api-report-react_ssr.api.md +++ b/.api-reports/api-report-react_ssr.api.md @@ -998,16 +998,18 @@ interface MaskOperationOptions { // @public (undocumented) type MaybeAsync = T | PromiseLike; -// Warning: (ae-forgotten-export) The symbol "RemoveMaskedMarker" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "DataMasking" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "RemoveMaskedMarker" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ContainsFragmentsRefs" needs to be exported by the entry point index.d.ts // // @public -type MaybeMasked = TData extends any ? true extends IsAny ? TData : TData extends { +type MaybeMasked = DataMasking extends { + mode: "unmask"; +} ? TData extends any ? true extends IsAny ? TData : TData extends { __masked?: true; -} ? Prettify> : DataMasking extends { - enabled: true; -} ? TData : true extends ContainsFragmentsRefs ? Unmasked : TData : never; +} ? Prettify> : true extends ContainsFragmentsRefs ? Unmasked : TData : never : DataMasking extends { + mode: "preserveTypes"; +} ? TData : TData; // Warning: (ae-forgotten-export) The symbol "CombineIntersection" needs to be exported by the entry point index.d.ts // diff --git a/.api-reports/api-report-testing.api.md b/.api-reports/api-report-testing.api.md index 0c7b64d8eee..18bbb81892d 100644 --- a/.api-reports/api-report-testing.api.md +++ b/.api-reports/api-report-testing.api.md @@ -987,16 +987,18 @@ interface MaskOperationOptions { // @public (undocumented) type MaybeAsync = T | PromiseLike; -// Warning: (ae-forgotten-export) The symbol "RemoveMaskedMarker" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "DataMasking" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "RemoveMaskedMarker" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ContainsFragmentsRefs" needs to be exported by the entry point index.d.ts // // @public -type MaybeMasked = TData extends any ? true extends IsAny ? TData : TData extends { +type MaybeMasked = DataMasking extends { + mode: "unmask"; +} ? TData extends any ? true extends IsAny ? TData : TData extends { __masked?: true; -} ? Prettify> : DataMasking extends { - enabled: true; -} ? TData : true extends ContainsFragmentsRefs ? Unmasked : TData : never; +} ? Prettify> : true extends ContainsFragmentsRefs ? Unmasked : TData : never : DataMasking extends { + mode: "preserveTypes"; +} ? TData : TData; // Warning: (ae-forgotten-export) The symbol "CombineIntersection" needs to be exported by the entry point index.d.ts // diff --git a/.api-reports/api-report-testing_core.api.md b/.api-reports/api-report-testing_core.api.md index d46a9f55d1f..9b98b8e17de 100644 --- a/.api-reports/api-report-testing_core.api.md +++ b/.api-reports/api-report-testing_core.api.md @@ -986,16 +986,18 @@ interface MaskOperationOptions { // @public (undocumented) type MaybeAsync = T | PromiseLike; -// Warning: (ae-forgotten-export) The symbol "RemoveMaskedMarker" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "DataMasking" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "RemoveMaskedMarker" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ContainsFragmentsRefs" needs to be exported by the entry point index.d.ts // // @public -type MaybeMasked = TData extends any ? true extends IsAny ? TData : TData extends { +type MaybeMasked = DataMasking extends { + mode: "unmask"; +} ? TData extends any ? true extends IsAny ? TData : TData extends { __masked?: true; -} ? Prettify> : DataMasking extends { - enabled: true; -} ? TData : true extends ContainsFragmentsRefs ? Unmasked : TData : never; +} ? Prettify> : true extends ContainsFragmentsRefs ? Unmasked : TData : never : DataMasking extends { + mode: "preserveTypes"; +} ? TData : TData; // Warning: (ae-forgotten-export) The symbol "CombineIntersection" needs to be exported by the entry point index.d.ts // diff --git a/.api-reports/api-report-utilities.api.md b/.api-reports/api-report-utilities.api.md index a09ee2ed138..6905e929046 100644 --- a/.api-reports/api-report-utilities.api.md +++ b/.api-reports/api-report-utilities.api.md @@ -1695,16 +1695,18 @@ type MaybeAsync = T | PromiseLike; // @public (undocumented) export function maybeDeepFreeze(obj: T): T; -// Warning: (ae-forgotten-export) The symbol "RemoveMaskedMarker" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "DataMasking" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "RemoveMaskedMarker" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ContainsFragmentsRefs" needs to be exported by the entry point index.d.ts // // @public -type MaybeMasked = TData extends any ? true extends IsAny ? TData : TData extends { +type MaybeMasked = DataMasking extends { + mode: "unmask"; +} ? TData extends any ? true extends IsAny ? TData : TData extends { __masked?: true; -} ? Prettify> : DataMasking extends { - enabled: true; -} ? TData : true extends ContainsFragmentsRefs ? Unmasked : TData : never; +} ? Prettify> : true extends ContainsFragmentsRefs ? Unmasked : TData : never : DataMasking extends { + mode: "preserveTypes"; +} ? TData : TData; // @public (undocumented) export function mergeDeep(...sources: T): TupleToIntersection; diff --git a/.api-reports/api-report.api.md b/.api-reports/api-report.api.md index 850d6e603b9..42e41d0e105 100644 --- a/.api-reports/api-report.api.md +++ b/.api-reports/api-report.api.md @@ -1593,11 +1593,13 @@ type MaybeAsync = T | PromiseLike; // Warning: (ae-forgotten-export) The symbol "ContainsFragmentsRefs" needs to be exported by the entry point index.d.ts // // @public -export type MaybeMasked = TData extends any ? true extends IsAny ? TData : TData extends { +export type MaybeMasked = DataMasking extends { + mode: "unmask"; +} ? TData extends any ? true extends IsAny ? TData : TData extends { __masked?: true; -} ? Prettify> : DataMasking extends { - enabled: true; -} ? TData : true extends ContainsFragmentsRefs ? Unmasked : TData : never; +} ? Prettify> : true extends ContainsFragmentsRefs ? Unmasked : TData : never : DataMasking extends { + mode: "preserveTypes"; +} ? TData : TData; // @public (undocumented) export interface MergeInfo { @@ -3082,7 +3084,7 @@ export interface UseReadQueryResult { export function useSubscription(subscription: DocumentNode | TypedDocumentNode, options?: SubscriptionHookOptions, NoInfer_2>): { restart: () => void; loading: boolean; - data?: MaybeMasked | undefined; + data?: TData | undefined; error?: ApolloError; variables?: TVariables | undefined; }; diff --git a/.changeset/cool-pants-smash.md b/.changeset/cool-pants-smash.md new file mode 100644 index 00000000000..92b7e9d6874 --- /dev/null +++ b/.changeset/cool-pants-smash.md @@ -0,0 +1,40 @@ +--- +"@apollo/client": patch +--- + +Changes the default behavior of the `MaybeMasked` type to preserve types unless otherwise specified. This change makes it easier to upgrade from older versions of the client where types could have unexpectedly changed in the application due to the default of trying to unwrap types into unmasked types. This change also fixes the compilation performance regression experienced when simply upgrading the client since types are now preserved by default. + +A new `mode` option has now been introduced to allow for the old behavior. See the next section on migrating if you wish to maintain the old default behavior after upgrading to this version. + +### Migrating from <= v3.12.4 + +If you've adopted data masking and have opted in to using masked types by setting the `enabled` property to `true`, you can remove this configuration entirely: + +```diff +-declare module "@apollo/client" { +- interface DataMasking { +- mode: "unmask" +- } +-} +``` + +If you prefer to specify the behavior explicitly, change the property from `enabled: true`, to `mode: "preserveTypes"`: + +```diff +declare module "@apollo/client" { + interface DataMasking { +- enabled: true ++ mode: "preserveTypes" + } +} +``` + +If you rely on the default behavior in 3.12.4 or below and would like to continue to use unmasked types by default, set the `mode` to `unmask`: + +```ts +declare module "@apollo/client" { + interface DataMasking { + mode: "unmask" + } +} +``` diff --git a/docs/source/data/fragments.mdx b/docs/source/data/fragments.mdx index 0faf4fcca73..ee6ccd596f0 100644 --- a/docs/source/data/fragments.mdx +++ b/docs/source/data/fragments.mdx @@ -837,12 +837,12 @@ It's important that the parent query or fragment selects any [`keyFields`](../ca ### Nesting fragments in other fragments +As your UI grows in complexity, it is common to split up components into smaller, more reusable chunks. As a result you may end up with more deeply nested components that have their own data requirements. Much like queries, we can nest fragments within other fragments. + -You can nest fragments with or without data masking (for an example, see the section on [colocating fragments](#colocating-fragments).) The following section describes how you can use masking in components with `useFragment`. +You can nest fragments with or without data masking (for an example, see the section on [colocating fragments](#colocating-fragments).) This section describes how you can use masking in components with `useFragment`. -As your UI grows in complexity, it is common to split up components into smaller, more reusable chunks. As a result you may end up with more deeply nested components that have their own data requirements. Much like queries, we can nest fragments within other fragments. - Let's add a `Comment` component that will be used by `PostDetails` to render the `topComment` for the post. ```jsx title="Comment.jsx" @@ -1104,9 +1104,7 @@ type GetCurrentUserQuery = { #### Generating masked types -You generate masked types with either the [`typescript-operations` plugin](https://the-guild.dev/graphql/codegen/plugins/typescript/typescript-operations) or the [client preset](https://the-guild.dev/graphql/codegen/plugins/presets/preset-client). You can generate masked types at any stage in the adoption process, even before enabling `dataMasking` in your client instance. Until you [opt in to use masked types](#opting-in-to-use-masked-types), the client unwraps them and provides the full operation type. - -The following sections show how to configure GraphQL Codegen to output masked types. +You generate masked types with either the [`typescript-operations` plugin](https://the-guild.dev/graphql/codegen/plugins/typescript/typescript-operations) or the [client preset](https://the-guild.dev/graphql/codegen/plugins/presets/preset-client). The following sections show how to configure GraphQL Codegen to output masked types. ##### With the `typescript-operations` plugin @@ -1140,53 +1138,47 @@ const config: CodegenConfig = { Support for the `@unmask` directive was introduced with `@graphql-codegen/client-preset` [v4.5.1](https://github.com/dotansimha/graphql-code-generator/releases/tag/release-1732308151614) -Add the following configuration to your GraphQL Codegen config. - - -The [Fragment Masking](https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#fragment-masking) feature in the client preset is incompatible with Apollo Client's data masking feature. You need to [turn off Fragment Masking](https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#how-to-disable-fragment-masking) in your configuration (included below). If you use the [generated `useFragment` function](https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#the-usefragment-helper), you must use Apollo Client's [`useFragment`](#usefragment) hook instead. - - -```ts title="codegen.ts" -const config: CodegenConfig = { - // ... - generates: { - "path/to/gql/": { - preset: "client", - presetConfig: { - // ... - // disables the incompatible GraphQL Codegen fragment masking feature - fragmentMasking: false, - customDirectives: { - apolloUnmask: true +You can't use the `client-preset` [Fragment Masking](https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#fragment-masking) and Apollo Client's data masking features simultaneously. +The incompatibility between the features is in runtime behavior only. +Apollo's data masking uses the same type output generated by CodeGen's Fragment Masking feature. + +To migrate from CodeGen's fragment masking feature to Apollo Client's data masking, follow these steps: + +1. Replace the [generated `useFragment` function](https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#the-usefragment-helper), with Apollo Client's [`useFragment`](#usefragment) hook. +2. [Turn off Fragment Masking](https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#how-to-disable-fragment-masking) in your GraphQL Codegen config, along with these additions: + + ```ts title="codegen.ts" + const config: CodegenConfig = { + // ... + generates: { + "path/to/gql/": { + preset: "client", + presetConfig: { + // ... + // disables the incompatible GraphQL Codegen fragment masking feature + fragmentMasking: false, + customDirectives: { + apolloUnmask: true + } + }, + config: { + inlineFragmentTypes: "mask", + } } - }, - config: { - inlineFragmentTypes: "mask", } } - } -} -``` - -#### Opting in to use masked types - - -We recommend that you opt in to use masked types only after you've [enabled `dataMasking`](#enabling-data-masking) in your `ApolloClient` instance. - - -By default, the client unwraps operation types and provides the full operation result type. To prevent this behavior and have the client use masked types, you need to opt in. This strategy allows for [incremental adoption](#incremental-adoption-in-an-existing-application) in your application and avoids the need to update large parts of your application to satisfy the change in types. + ``` -You can opt in to use the masked types in one of two ways. -##### Opting in globally +3. [Enable data masking](#enabling-data-masking) in Apollo Client. -To turn on masked types for your whole application at once, modify the `DataMasking` exported type from `@apollo/client` using TypeScript's [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html) ability. + +#### Setting a types mode for masked types + - -We recommend this approach for most cases. Use this approach with the [`@unmask` directive](./directives#unmask) for the best path to [incremental adoption](#incremental-adoption-in-an-existing-application). - +Apollo Client provides different modes to work with operation types throughout your application. You change the mode by modifying the `DataMasking` exported type from the `@apollo/client` package using TypeScript's [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html) ability. Specifying a mode is optional and only needed when you prefer to use a different mode other than the default. -First, create a TypeScript file that will be used to modify the `DataMasking` type. +To modify the data masking mode used in the client, first create a TypeScript file that will be used to modify the `DataMasking` type. ```ts title="apollo-client.d.ts" // This import is necessary to ensure all Apollo Client imports @@ -1195,7 +1187,7 @@ import '@apollo/client'; declare module "@apollo/client" { interface DataMasking { - enabled: true; + mode: "preserveTypes"; } } ``` @@ -1204,9 +1196,25 @@ declare module "@apollo/client" { This example uses `apollo-client.d.ts` as the file name to make it easily identifiable. You can name this file as you wish. -With `enabled` set to `true`, any request-based API will type `data` using the masked type and prevent the client from unwrapping it. +##### Modes -##### Opting in per operation + +Prior to Apollo Client version 3.12.5, the default was to [unmask](#unmask) the types. If you upgrade to 3.12.5 or later and wish to maintain the same default as 3.12.4 or below, set the mode to [`unmask`](#unmask). See pull request [#12252](https://github.com/apollographql/apollo-client/pull/12252) for more details on this change, including a section on migrating from {"<="} 3.12.4. + + +###### `preserveTypes` (default) + +The default `preserveTypes` mode makes no modification to the operation types regardless of whether the type definitions are masked or unmasked. This provides a simpler upgrade path when you're ready to [incrementally adopt](#incremental-adoption-in-an-existing-application) data masking. + +###### `unmask` + +Setting the mode to `unmask` will unwrap masked types and provide the full result type. Use this mode when you [generate masked types](#generating-masked-types) but need to maintain access to the full result type, such as using this with [per-operation masked types](#using-per-operation-masked-types). + +##### Using per-operation masked types + + +This is only useful if you have changed the types mode to `unmask`. Using this with `preserveTypes` has no effect. + If you prefer an incremental approach, you can opt in to use masked types per operation. This can be useful when your application creates multiple Apollo Client instances where only a subset enables data masking. @@ -1315,7 +1323,7 @@ In this example, the `GetPosts` query selects enough fields to satisfy the `Post On rare occasions, you may need access to the unmasked type of a particular operation. Apollo Client provides the `Unmasked` helper type that unwraps masked types and removes meta information on the type. -This is the same helper type the client uses when unwrapping types while data masking is turned off or for APIs that use the full result. +This is the same helper type the client uses when unwrapping types while the TypeScript data masking `mode` is set to `unmask` or for APIs that use the full result. ```ts @@ -1419,9 +1427,9 @@ new ApolloClient({ > Enabling data masking early in the adoption process makes it much easier to adopt for newly added queries and fragments since masking becomes the default behavior. Ideally data masking is enabled in the same pull request as the `@unmask` changes to ensure that no new queries and fragments are introduced to the codebase without the `@unmask` modifications applied. -#### 3. Generate and opt in to use masked types +#### 3. Generate masked types -If you are using TypeScript in your application, you will need to update your GraphQL Codegen configuration to [generate masked types](#generating-masked-types). Once you generate masked types, [opt in](#opting-in-to-use-masked-types) to use the masking types with your client. +If you are using TypeScript in your application, you will need to update your GraphQL Codegen configuration to [generate masked types](#generating-masked-types). Learn more about using TypeScript with data masking in the ["Using with TypeScript"](#using-with-typescript) section. diff --git a/src/masking/__benches__/types.bench.ts b/src/masking/__benches__/types.bench.ts index 0ae97b7edc4..97a67e69b4e 100644 --- a/src/masking/__benches__/types.bench.ts +++ b/src/masking/__benches__/types.bench.ts @@ -4,6 +4,8 @@ import { expectTypeOf } from "expect-type"; import type { DeepPartial } from "../../utilities/index.js"; import { setup } from "@ark/attest"; +import type { ContainsFragmentsRefs } from "../internal/types.js"; +import type { TypedDocumentNode } from "../../index.js"; setup({ updateSnapshots: !process.env.CI, @@ -566,7 +568,8 @@ test("detects `$fragmentRefs` on types with index signatures", (prefix) => { }).types([6, "instantiations"]); bench(prefix + "functionality", () => { - const x = {} as MaybeMasked; + const x = {} as Unmasked; + const y = {} as ContainsFragmentsRefs; expectTypeOf(x).branded.toEqualTypeOf<{ __typename: "Foo"; @@ -575,6 +578,7 @@ test("detects `$fragmentRefs` on types with index signatures", (prefix) => { foo: string; structuredMetadata: StructuredMetadata; }>(); + expectTypeOf(y).toEqualTypeOf(); }); }); @@ -592,3 +596,34 @@ test("recursive types: no error 'Type instantiation is excessively deep and poss expectTypeOf(x).branded.toEqualTypeOf(); }); }); + +test("MaybeMasked can be called with a generic if `mode` is not set to `unmask`", (prefix) => { + function withGenericResult( + arg: TypedDocumentNode + ) { + bench(prefix + "Result generic - instantiations", () => { + const maybeMasked: MaybeMasked = arg; + return maybeMasked; + }).types(); + + bench(prefix + "Result generic - functionality", () => { + const maybeMasked: MaybeMasked = arg; + expectTypeOf(maybeMasked).toEqualTypeOf(arg); + }); + } + function withGenericDocument(arg: T) { + bench(prefix + "Result generic - instantiations", () => { + const maybeMasked: MaybeMasked = arg; + return maybeMasked; + }).types(); + + bench(prefix + "Result generic - functionality", () => { + const maybeMasked: MaybeMasked = arg; + // cannot use unresolved generic with `expectTypeOf` here so we just try an assignment the other way round + const test: T = maybeMasked; + return test; + }); + } + withGenericResult({} as any); + withGenericDocument({} as any); +}); diff --git a/src/masking/types.ts b/src/masking/types.ts index b50617d6d64..f2c5f4cb4ca 100644 --- a/src/masking/types.ts +++ b/src/masking/types.ts @@ -39,15 +39,17 @@ export type FragmentType = * enabled. */ export type MaybeMasked = - // distribute TData - in case of a union, do the next steps for each member - TData extends any ? - // prevent "Type instantiation is excessively deep and possibly infinite." - true extends IsAny ? TData - : TData extends { __masked?: true } ? Prettify> - : DataMasking extends { enabled: true } ? TData - : true extends ContainsFragmentsRefs ? Unmasked - : TData - : never; + DataMasking extends { mode: "unmask" } ? + // distribute TData - in case of a union, do the next steps for each member + TData extends any ? + // prevent "Type instantiation is excessively deep and possibly infinite." + true extends IsAny ? TData + : TData extends { __masked?: true } ? Prettify> + : true extends ContainsFragmentsRefs ? Unmasked + : TData + : never + : DataMasking extends { mode: "preserveTypes" } ? TData + : TData; /** * Unmasks a type to provide its full result. diff --git a/src/react/hooks/__tests__/useBackgroundQuery.test.tsx b/src/react/hooks/__tests__/useBackgroundQuery.test.tsx index 7e235ea7132..6ce0564cc63 100644 --- a/src/react/hooks/__tests__/useBackgroundQuery.test.tsx +++ b/src/react/hooks/__tests__/useBackgroundQuery.test.tsx @@ -7250,7 +7250,7 @@ describe.skip("type tests", () => { const [queryRef] = useBackgroundQuery(maskedQuery); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf>(); expectTypeOf(data).not.toEqualTypeOf(); } @@ -7261,8 +7261,8 @@ describe.skip("type tests", () => { >(maskedQuery); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf(); - expectTypeOf(data).not.toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).not.toEqualTypeOf(); } { @@ -7272,7 +7272,7 @@ describe.skip("type tests", () => { >(maskedQuery); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf>(); expectTypeOf(data).not.toEqualTypeOf(); } }); @@ -7306,7 +7306,7 @@ describe.skip("type tests", () => { const [queryRef] = useBackgroundQuery(maskedQuery); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf>(); expectTypeOf(data).not.toEqualTypeOf(); } @@ -7317,8 +7317,8 @@ describe.skip("type tests", () => { >(maskedQuery); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf(); - expectTypeOf(data).not.toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).not.toEqualTypeOf(); } { @@ -7328,7 +7328,7 @@ describe.skip("type tests", () => { >(maskedQuery); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf>(); expectTypeOf(data).not.toEqualTypeOf(); } }); @@ -7360,7 +7360,9 @@ describe.skip("type tests", () => { }); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf< + Masked | undefined + >(); expectTypeOf(data).not.toEqualTypeOf< UnmaskedVariablesCaseData | undefined >(); @@ -7373,9 +7375,9 @@ describe.skip("type tests", () => { >(maskedQuery, { errorPolicy: "all" }); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf(); expectTypeOf(data).not.toEqualTypeOf< - MaskedVariablesCaseData | undefined + UnmaskedVariablesCaseData | undefined >(); } @@ -7386,7 +7388,9 @@ describe.skip("type tests", () => { >(maskedQuery, { errorPolicy: "all" }); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf< + Masked | undefined + >(); expectTypeOf(data).not.toEqualTypeOf< UnmaskedVariablesCaseData | undefined >(); @@ -7420,7 +7424,7 @@ describe.skip("type tests", () => { }); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf>(); expectTypeOf(data).not.toEqualTypeOf(); } @@ -7431,8 +7435,8 @@ describe.skip("type tests", () => { >(maskedQuery, { errorPolicy: "none" }); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf(); - expectTypeOf(data).not.toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).not.toEqualTypeOf(); } { @@ -7442,7 +7446,7 @@ describe.skip("type tests", () => { >(maskedQuery, { errorPolicy: "none" }); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf>(); expectTypeOf(data).not.toEqualTypeOf(); } }); @@ -7478,7 +7482,9 @@ describe.skip("type tests", () => { }); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf>(); + expectTypeOf(data).toEqualTypeOf< + DeepPartial> + >(); expectTypeOf(data).not.toEqualTypeOf< DeepPartial >(); @@ -7491,11 +7497,9 @@ describe.skip("type tests", () => { >(maskedQuery, { returnPartialData: true }); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf< - DeepPartial - >(); + expectTypeOf(data).toEqualTypeOf>(); expectTypeOf(data).not.toEqualTypeOf< - DeepPartial + DeepPartial >(); } @@ -7506,7 +7510,9 @@ describe.skip("type tests", () => { >(maskedQuery, { returnPartialData: true }); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf>(); + expectTypeOf(data).toEqualTypeOf< + DeepPartial> + >(); expectTypeOf(data).not.toEqualTypeOf< DeepPartial >(); @@ -7544,7 +7550,7 @@ describe.skip("type tests", () => { }); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf>(); expectTypeOf(data).not.toEqualTypeOf(); } @@ -7555,8 +7561,8 @@ describe.skip("type tests", () => { >(maskedQuery, { returnPartialData: false }); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf(); - expectTypeOf(data).not.toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).not.toEqualTypeOf(); } { @@ -7566,7 +7572,7 @@ describe.skip("type tests", () => { >(maskedQuery, { returnPartialData: false }); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf>(); expectTypeOf(data).not.toEqualTypeOf(); } }); @@ -7602,7 +7608,7 @@ describe.skip("type tests", () => { }); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf>(); expectTypeOf(data).not.toEqualTypeOf(); } @@ -7613,8 +7619,8 @@ describe.skip("type tests", () => { >(maskedQuery, { fetchPolicy: "no-cache" }); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf(); - expectTypeOf(data).not.toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).not.toEqualTypeOf(); } { @@ -7624,7 +7630,7 @@ describe.skip("type tests", () => { >(maskedQuery, { fetchPolicy: "no-cache" }); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf>(); expectTypeOf(data).not.toEqualTypeOf(); } }); @@ -7675,7 +7681,7 @@ describe.skip("type tests", () => { const { data } = useReadQuery(queryRef); expectTypeOf(data).toEqualTypeOf< - DeepPartial | undefined + DeepPartial> | undefined >(); expectTypeOf(data).not.toEqualTypeOf< DeepPartial | undefined @@ -7690,10 +7696,10 @@ describe.skip("type tests", () => { const { data } = useReadQuery(queryRef); expectTypeOf(data).toEqualTypeOf< - DeepPartial | undefined + DeepPartial | undefined >(); expectTypeOf(data).not.toEqualTypeOf< - DeepPartial | undefined + DeepPartial | undefined >(); } @@ -7705,7 +7711,7 @@ describe.skip("type tests", () => { const { data } = useReadQuery(queryRef); expectTypeOf(data).toEqualTypeOf< - DeepPartial | undefined + DeepPartial> | undefined >(); expectTypeOf(data).not.toEqualTypeOf< DeepPartial | undefined @@ -7754,7 +7760,9 @@ describe.skip("type tests", () => { }); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf>(); + expectTypeOf(data).toEqualTypeOf< + DeepPartial> + >(); expectTypeOf(data).not.toEqualTypeOf< DeepPartial >(); @@ -7767,11 +7775,9 @@ describe.skip("type tests", () => { >(maskedQuery, { returnPartialData: true, errorPolicy: "none" }); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf< - DeepPartial - >(); + expectTypeOf(data).toEqualTypeOf>(); expectTypeOf(data).not.toEqualTypeOf< - DeepPartial + DeepPartial >(); } @@ -7782,7 +7788,9 @@ describe.skip("type tests", () => { >(maskedQuery, { returnPartialData: true, errorPolicy: "none" }); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf>(); + expectTypeOf(data).toEqualTypeOf< + DeepPartial> + >(); expectTypeOf(data).not.toEqualTypeOf< DeepPartial >(); @@ -7826,7 +7834,9 @@ describe.skip("type tests", () => { }); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf>(); + expectTypeOf(data).toEqualTypeOf< + DeepPartial> + >(); expectTypeOf(data).not.toEqualTypeOf< DeepPartial >(); @@ -7843,11 +7853,9 @@ describe.skip("type tests", () => { }); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf< - DeepPartial - >(); + expectTypeOf(data).toEqualTypeOf>(); expectTypeOf(data).not.toEqualTypeOf< - DeepPartial + DeepPartial >(); } @@ -7862,7 +7870,9 @@ describe.skip("type tests", () => { }); const { data } = useReadQuery(queryRef); - expectTypeOf(data).toEqualTypeOf>(); + expectTypeOf(data).toEqualTypeOf< + DeepPartial> + >(); expectTypeOf(data).not.toEqualTypeOf< DeepPartial >(); @@ -8260,7 +8270,9 @@ describe.skip("type tests", () => { const result = await refetch(); - expectTypeOf(result.data).toEqualTypeOf(); + expectTypeOf(result.data).toEqualTypeOf< + Masked + >(); expectTypeOf(result.data).not.toEqualTypeOf(); } @@ -8269,8 +8281,8 @@ describe.skip("type tests", () => { const result = await refetch(); - expectTypeOf(result.data).toEqualTypeOf(); - expectTypeOf(result.data).not.toEqualTypeOf(); + expectTypeOf(result.data).toEqualTypeOf(); + expectTypeOf(result.data).not.toEqualTypeOf(); } }); @@ -8296,7 +8308,9 @@ describe.skip("type tests", () => { }, }); - expectTypeOf(result.data).toEqualTypeOf(); + expectTypeOf(result.data).toEqualTypeOf< + Masked + >(); expectTypeOf(result.data).not.toEqualTypeOf(); } @@ -8319,8 +8333,8 @@ describe.skip("type tests", () => { }, }); - expectTypeOf(result.data).toEqualTypeOf(); - expectTypeOf(result.data).not.toEqualTypeOf(); + expectTypeOf(result.data).toEqualTypeOf(); + expectTypeOf(result.data).not.toEqualTypeOf(); } }); diff --git a/src/react/hooks/__tests__/useLazyQuery.test.tsx b/src/react/hooks/__tests__/useLazyQuery.test.tsx index e678095d88b..ed99fdd8905 100644 --- a/src/react/hooks/__tests__/useLazyQuery.test.tsx +++ b/src/react/hooks/__tests__/useLazyQuery.test.tsx @@ -25,7 +25,7 @@ import { import { useLazyQuery } from "../useLazyQuery"; import { QueryResult } from "../../types/types"; import { InvariantError } from "../../../utilities/globals"; -import { MaskedDocumentNode } from "../../../masking"; +import { Masked, MaskedDocumentNode } from "../../../masking"; import { expectTypeOf } from "expect-type"; import { disableActEnvironment, @@ -2567,12 +2567,12 @@ describe.skip("Type Tests", () => { { data, previousData, subscribeToMore, fetchMore, refetch, updateQuery }, ] = useLazyQuery(query, { onCompleted(data) { - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf>(); }, }); - expectTypeOf(data).toEqualTypeOf(); - expectTypeOf(previousData).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf | undefined>(); + expectTypeOf(previousData).toEqualTypeOf | undefined>(); subscribeToMore({ document: gql`` as TypedDocumentNode, @@ -2595,7 +2595,7 @@ describe.skip("Type Tests", () => { { const { data } = await execute(); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf | undefined>(); } { @@ -2609,17 +2609,17 @@ describe.skip("Type Tests", () => { }, }); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf>(); } { const { data } = await refetch(); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf>(); } }); - test("uses unmasked types when using TypedDocumentNode", async () => { + test("uses unmodified types when using TypedDocumentNode", async () => { type UserFieldsFragment = { __typename: "User"; age: number; @@ -2666,12 +2666,12 @@ describe.skip("Type Tests", () => { { data, previousData, fetchMore, refetch, subscribeToMore, updateQuery }, ] = useLazyQuery(query, { onCompleted(data) { - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf(); }, }); - expectTypeOf(data).toEqualTypeOf(); - expectTypeOf(previousData).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(previousData).toEqualTypeOf(); subscribeToMore({ document: gql`` as TypedDocumentNode, @@ -2694,7 +2694,7 @@ describe.skip("Type Tests", () => { { const { data } = await execute(); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf(); } { @@ -2708,13 +2708,13 @@ describe.skip("Type Tests", () => { }, }); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf(); } { const { data } = await refetch(); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf(); } }); }); diff --git a/src/react/hooks/__tests__/useSubscription.test.tsx b/src/react/hooks/__tests__/useSubscription.test.tsx index f3aa2099bfd..2290401b550 100644 --- a/src/react/hooks/__tests__/useSubscription.test.tsx +++ b/src/react/hooks/__tests__/useSubscription.test.tsx @@ -20,7 +20,7 @@ import { ErrorBoundary } from "react-error-boundary"; import { MockedSubscriptionResult } from "../../../testing/core/mocking/mockSubscriptionLink"; import { GraphQLError } from "graphql"; import { InvariantError } from "ts-invariant"; -import { MaskedDocumentNode } from "../../../masking"; +import { Masked, MaskedDocumentNode } from "../../../masking"; import { expectTypeOf } from "expect-type"; import { disableActEnvironment, @@ -2433,19 +2433,21 @@ describe.skip("Type Tests", () => { const { data } = useSubscription(subscription, { onData: ({ data }) => { - expectTypeOf(data.data).toEqualTypeOf(); + expectTypeOf(data.data).toEqualTypeOf< + Masked | undefined + >(); }, onSubscriptionData: ({ subscriptionData }) => { expectTypeOf(subscriptionData.data).toEqualTypeOf< - Subscription | undefined + Masked | undefined >(); }, }); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf | undefined>(); }); - test("uses unmasked types when using TypedDocumentNode", async () => { + test("uses unmodified type when using TypedDocumentNode", async () => { type UserFieldsFragment = { __typename: "User"; age: number; @@ -2459,30 +2461,19 @@ describe.skip("Type Tests", () => { } & { " $fragmentRefs"?: { UserFieldsFragment: UserFieldsFragment } }; } - interface UnmaskedSubscription { - userUpdated: { - __typename: "User"; - id: string; - name: string; - age: number; - }; - } - const subscription: TypedDocumentNode = gql``; const { data } = useSubscription(subscription, { onData: ({ data }) => { - expectTypeOf(data.data).toEqualTypeOf< - UnmaskedSubscription | undefined - >(); + expectTypeOf(data.data).toEqualTypeOf(); }, onSubscriptionData: ({ subscriptionData }) => { expectTypeOf(subscriptionData.data).toEqualTypeOf< - UnmaskedSubscription | undefined + Subscription | undefined >(); }, }); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf(); }); }); diff --git a/src/react/hooks/__tests__/useSuspenseQuery.test.tsx b/src/react/hooks/__tests__/useSuspenseQuery.test.tsx index 72c2e4050e8..ca644a4ea2f 100644 --- a/src/react/hooks/__tests__/useSuspenseQuery.test.tsx +++ b/src/react/hooks/__tests__/useSuspenseQuery.test.tsx @@ -11816,7 +11816,7 @@ describe("useSuspenseQuery", () => { { const { data } = useSuspenseQuery(maskedQuery); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf>(); expectTypeOf(data).not.toEqualTypeOf(); } @@ -11826,8 +11826,8 @@ describe("useSuspenseQuery", () => { VariablesCaseVariables >(maskedQuery); - expectTypeOf(data).toEqualTypeOf(); - expectTypeOf(data).not.toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).not.toEqualTypeOf(); } { @@ -11836,7 +11836,7 @@ describe("useSuspenseQuery", () => { VariablesCaseVariables >(maskedQuery); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf>(); expectTypeOf(data).not.toEqualTypeOf(); } }); @@ -11868,7 +11868,9 @@ describe("useSuspenseQuery", () => { errorPolicy: "ignore", }); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf< + Masked | undefined + >(); expectTypeOf(data).not.toEqualTypeOf< UnmaskedVariablesCaseData | undefined >(); @@ -11880,11 +11882,9 @@ describe("useSuspenseQuery", () => { VariablesCaseVariables >(maskedQuery, { errorPolicy: "ignore" }); - expectTypeOf(data).toEqualTypeOf< - UnmaskedVariablesCaseData | undefined - >(); + expectTypeOf(data).toEqualTypeOf(); expectTypeOf(data).not.toEqualTypeOf< - MaskedVariablesCaseData | undefined + UnmaskedVariablesCaseData | undefined >(); } @@ -11894,7 +11894,9 @@ describe("useSuspenseQuery", () => { VariablesCaseVariables >(maskedQuery, { errorPolicy: "ignore" }); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf< + Masked | undefined + >(); expectTypeOf(data).not.toEqualTypeOf< UnmaskedVariablesCaseData | undefined >(); @@ -11926,7 +11928,9 @@ describe("useSuspenseQuery", () => { { const { data } = useSuspenseQuery(maskedQuery, { errorPolicy: "all" }); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf< + Masked | undefined + >(); expectTypeOf(data).not.toEqualTypeOf< UnmaskedVariablesCaseData | undefined >(); @@ -11938,11 +11942,9 @@ describe("useSuspenseQuery", () => { VariablesCaseVariables >(maskedQuery, { errorPolicy: "all" }); - expectTypeOf(data).toEqualTypeOf< - UnmaskedVariablesCaseData | undefined - >(); + expectTypeOf(data).toEqualTypeOf(); expectTypeOf(data).not.toEqualTypeOf< - MaskedVariablesCaseData | undefined + UnmaskedVariablesCaseData | undefined >(); } @@ -11952,7 +11954,9 @@ describe("useSuspenseQuery", () => { VariablesCaseVariables >(maskedQuery, { errorPolicy: "all" }); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf< + Masked | undefined + >(); expectTypeOf(data).not.toEqualTypeOf< UnmaskedVariablesCaseData | undefined >(); @@ -11984,7 +11988,7 @@ describe("useSuspenseQuery", () => { { const { data } = useSuspenseQuery(maskedQuery, { errorPolicy: "none" }); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf>(); expectTypeOf(data).not.toEqualTypeOf(); } @@ -11994,8 +11998,8 @@ describe("useSuspenseQuery", () => { VariablesCaseVariables >(maskedQuery, { errorPolicy: "none" }); - expectTypeOf(data).toEqualTypeOf(); - expectTypeOf(data).not.toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).not.toEqualTypeOf(); } { @@ -12004,7 +12008,7 @@ describe("useSuspenseQuery", () => { VariablesCaseVariables >(maskedQuery, { errorPolicy: "none" }); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf>(); expectTypeOf(data).not.toEqualTypeOf(); } }); @@ -12037,7 +12041,7 @@ describe("useSuspenseQuery", () => { }); expectTypeOf(data).toEqualTypeOf< - DeepPartial + DeepPartial> >(); expectTypeOf(data).not.toEqualTypeOf< DeepPartial @@ -12051,10 +12055,10 @@ describe("useSuspenseQuery", () => { >(maskedQuery, { returnPartialData: true }); expectTypeOf(data).toEqualTypeOf< - DeepPartial + DeepPartial >(); expectTypeOf(data).not.toEqualTypeOf< - DeepPartial + DeepPartial >(); } @@ -12065,7 +12069,7 @@ describe("useSuspenseQuery", () => { >(maskedQuery, { returnPartialData: true }); expectTypeOf(data).toEqualTypeOf< - DeepPartial + DeepPartial> >(); expectTypeOf(data).not.toEqualTypeOf< DeepPartial @@ -12104,7 +12108,7 @@ describe("useSuspenseQuery", () => { returnPartialData: false, }); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf>(); expectTypeOf(data).not.toEqualTypeOf(); } @@ -12114,8 +12118,8 @@ describe("useSuspenseQuery", () => { VariablesCaseVariables >(maskedQuery, { returnPartialData: false }); - expectTypeOf(data).toEqualTypeOf(); - expectTypeOf(data).not.toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).not.toEqualTypeOf(); } { @@ -12124,7 +12128,7 @@ describe("useSuspenseQuery", () => { VariablesCaseVariables >(maskedQuery, { returnPartialData: false }); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf>(); expectTypeOf(data).not.toEqualTypeOf(); } }); @@ -12168,7 +12172,9 @@ describe("useSuspenseQuery", () => { { const { data } = useSuspenseQuery(maskedQuery, { skip: true }); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf< + Masked | undefined + >(); expectTypeOf(data).not.toEqualTypeOf< UnmaskedVariablesCaseData | undefined >(); @@ -12180,11 +12186,9 @@ describe("useSuspenseQuery", () => { VariablesCaseVariables >(maskedQuery, { skip: true }); - expectTypeOf(data).toEqualTypeOf< - UnmaskedVariablesCaseData | undefined - >(); + expectTypeOf(data).toEqualTypeOf(); expectTypeOf(data).not.toEqualTypeOf< - MaskedVariablesCaseData | undefined + UnmaskedVariablesCaseData | undefined >(); } @@ -12194,7 +12198,9 @@ describe("useSuspenseQuery", () => { VariablesCaseVariables >(maskedQuery, { skip: true }); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf< + Masked | undefined + >(); expectTypeOf(data).not.toEqualTypeOf< UnmaskedVariablesCaseData | undefined >(); @@ -12210,7 +12216,9 @@ describe("useSuspenseQuery", () => { VariablesCaseVariables >(maskedQuery, { skip: options.skip }); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf< + Masked | undefined + >(); expectTypeOf(data).not.toEqualTypeOf< UnmaskedVariablesCaseData | undefined >(); @@ -12247,7 +12255,9 @@ describe("useSuspenseQuery", () => { options.skip ? skipToken : { variables: { id: "1" } } ); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf< + Masked | undefined + >(); expectTypeOf(data).not.toEqualTypeOf< UnmaskedVariablesCaseData | undefined >(); @@ -12259,11 +12269,9 @@ describe("useSuspenseQuery", () => { VariablesCaseVariables >(maskedQuery, options.skip ? skipToken : { variables: { id: "1" } }); - expectTypeOf(data).toEqualTypeOf< - UnmaskedVariablesCaseData | undefined - >(); + expectTypeOf(data).toEqualTypeOf(); expectTypeOf(data).not.toEqualTypeOf< - MaskedVariablesCaseData | undefined + UnmaskedVariablesCaseData | undefined >(); } @@ -12273,7 +12281,9 @@ describe("useSuspenseQuery", () => { VariablesCaseVariables >(maskedQuery, options.skip ? skipToken : { variables: { id: "1" } }); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf< + Masked | undefined + >(); expectTypeOf(data).not.toEqualTypeOf< UnmaskedVariablesCaseData | undefined >(); @@ -12310,7 +12320,9 @@ describe("useSuspenseQuery", () => { options.skip ? skipToken : undefined ); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf< + Masked | undefined + >(); expectTypeOf(data).not.toEqualTypeOf< UnmaskedVariablesCaseData | undefined >(); @@ -12322,11 +12334,9 @@ describe("useSuspenseQuery", () => { VariablesCaseVariables >(maskedQuery, options.skip ? skipToken : undefined); - expectTypeOf(data).toEqualTypeOf< - UnmaskedVariablesCaseData | undefined - >(); + expectTypeOf(data).toEqualTypeOf(); expectTypeOf(data).not.toEqualTypeOf< - MaskedVariablesCaseData | undefined + UnmaskedVariablesCaseData | undefined >(); } @@ -12336,7 +12346,9 @@ describe("useSuspenseQuery", () => { VariablesCaseVariables >(maskedQuery, options.skip ? skipToken : undefined); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf< + Masked | undefined + >(); expectTypeOf(data).not.toEqualTypeOf< UnmaskedVariablesCaseData | undefined >(); @@ -12378,7 +12390,7 @@ describe("useSuspenseQuery", () => { ); expectTypeOf(data).toEqualTypeOf< - DeepPartial | undefined + DeepPartial> | undefined >(); expectTypeOf(data).not.toEqualTypeOf< DeepPartial | undefined @@ -12392,10 +12404,10 @@ describe("useSuspenseQuery", () => { >(maskedQuery, options.skip ? skipToken : { returnPartialData: true }); expectTypeOf(data).toEqualTypeOf< - DeepPartial | undefined + DeepPartial | undefined >(); expectTypeOf(data).not.toEqualTypeOf< - DeepPartial | undefined + DeepPartial | undefined >(); } @@ -12406,7 +12418,7 @@ describe("useSuspenseQuery", () => { >(maskedQuery, options.skip ? skipToken : { returnPartialData: true }); expectTypeOf(data).toEqualTypeOf< - DeepPartial | undefined + DeepPartial> | undefined >(); expectTypeOf(data).not.toEqualTypeOf< DeepPartial | undefined @@ -12445,7 +12457,7 @@ describe("useSuspenseQuery", () => { fetchPolicy: "no-cache", }); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf>(); expectTypeOf(data).not.toEqualTypeOf(); } @@ -12455,8 +12467,8 @@ describe("useSuspenseQuery", () => { VariablesCaseVariables >(maskedQuery, { fetchPolicy: "no-cache" }); - expectTypeOf(data).toEqualTypeOf(); - expectTypeOf(data).not.toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).not.toEqualTypeOf(); } { @@ -12465,7 +12477,7 @@ describe("useSuspenseQuery", () => { VariablesCaseVariables >(maskedQuery, { fetchPolicy: "no-cache" }); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf>(); expectTypeOf(data).not.toEqualTypeOf(); } }); @@ -12516,7 +12528,7 @@ describe("useSuspenseQuery", () => { }); expectTypeOf(data).toEqualTypeOf< - DeepPartial | undefined + DeepPartial> | undefined >(); expectTypeOf(data).not.toEqualTypeOf< DeepPartial | undefined @@ -12557,7 +12569,7 @@ describe("useSuspenseQuery", () => { }); expectTypeOf(data).toEqualTypeOf< - DeepPartial | undefined + DeepPartial> | undefined >(); expectTypeOf(data).not.toEqualTypeOf< DeepPartial | undefined @@ -12595,7 +12607,9 @@ describe("useSuspenseQuery", () => { errorPolicy: "ignore", }); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf< + Masked | undefined + >(); expectTypeOf(data).not.toEqualTypeOf< UnmaskedVariablesCaseData | undefined >(); @@ -12630,7 +12644,9 @@ describe("useSuspenseQuery", () => { errorPolicy: "none", }); - expectTypeOf(data).toEqualTypeOf(); + expectTypeOf(data).toEqualTypeOf< + Masked | undefined + >(); expectTypeOf(data).not.toEqualTypeOf< UnmaskedVariablesCaseData | undefined >(); @@ -12673,7 +12689,7 @@ describe("useSuspenseQuery", () => { }); expectTypeOf(data).toEqualTypeOf< - DeepPartial | undefined + DeepPartial> | undefined >(); expectTypeOf(data).not.toEqualTypeOf< DeepPartial | undefined @@ -12715,7 +12731,7 @@ describe("useSuspenseQuery", () => { }); expectTypeOf(data).toEqualTypeOf< - DeepPartial + DeepPartial> >(); expectTypeOf(data).not.toEqualTypeOf< DeepPartial @@ -12733,10 +12749,10 @@ describe("useSuspenseQuery", () => { }); expectTypeOf(data).toEqualTypeOf< - DeepPartial + DeepPartial >(); expectTypeOf(data).not.toEqualTypeOf< - DeepPartial + DeepPartial >(); } @@ -12751,7 +12767,7 @@ describe("useSuspenseQuery", () => { }); expectTypeOf(data).toEqualTypeOf< - DeepPartial + DeepPartial> >(); expectTypeOf(data).not.toEqualTypeOf< DeepPartial @@ -12767,7 +12783,9 @@ describe("useSuspenseQuery", () => { const result = await refetch(); - expectTypeOf(result.data).toEqualTypeOf(); + expectTypeOf(result.data).toEqualTypeOf< + Masked + >(); expectTypeOf( result.data ).not.toEqualTypeOf(); @@ -12778,8 +12796,10 @@ describe("useSuspenseQuery", () => { const result = await refetch(); - expectTypeOf(result.data).toEqualTypeOf(); - expectTypeOf(result.data).not.toEqualTypeOf(); + expectTypeOf(result.data).toEqualTypeOf(); + expectTypeOf( + result.data + ).not.toEqualTypeOf(); } }); @@ -12807,7 +12827,9 @@ describe("useSuspenseQuery", () => { }, }); - expectTypeOf(result.data).toEqualTypeOf(); + expectTypeOf(result.data).toEqualTypeOf< + Masked + >(); expectTypeOf( result.data ).not.toEqualTypeOf(); @@ -12834,8 +12856,10 @@ describe("useSuspenseQuery", () => { }, }); - expectTypeOf(result.data).toEqualTypeOf(); - expectTypeOf(result.data).not.toEqualTypeOf(); + expectTypeOf(result.data).toEqualTypeOf(); + expectTypeOf( + result.data + ).not.toEqualTypeOf(); } });