diff --git a/.api-reports/api-report-cache.api.md b/.api-reports/api-report-cache.api.md index 243e585f909..56f7904f774 100644 --- a/.api-reports/api-report-cache.api.md +++ b/.api-reports/api-report-cache.api.md @@ -1044,13 +1044,13 @@ type Unmasked = TData extends object ? UnwrapFragmentRefs = string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { +type UnwrapFragmentRefs = TData extends any ? string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { " $fragmentRefs"?: infer FragmentRefs extends object; } ? Prettify<{ [K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs; } & CombineFragmentRefs> : never : TData extends object ? { [K in keyof TData]: UnwrapFragmentRefs; -} : TData; +} : TData : never; // @public export interface WatchFragmentOptions { diff --git a/.api-reports/api-report-core.api.md b/.api-reports/api-report-core.api.md index e46102d87c7..59ba909cab2 100644 --- a/.api-reports/api-report-core.api.md +++ b/.api-reports/api-report-core.api.md @@ -2341,13 +2341,13 @@ export type Unmasked = TData extends object ? UnwrapFragmentRefs = string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { +type UnwrapFragmentRefs = TData extends any ? string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { " $fragmentRefs"?: infer FragmentRefs extends object; } ? Prettify<{ [K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs; } & CombineFragmentRefs> : never : TData extends object ? { [K in keyof TData]: UnwrapFragmentRefs; -} : TData; +} : TData : never; // @public (undocumented) type UpdateQueries = MutationOptions["updateQueries"]; diff --git a/.api-reports/api-report-masking.api.md b/.api-reports/api-report-masking.api.md index 5a1f96fb2b5..225e1e62877 100644 --- a/.api-reports/api-report-masking.api.md +++ b/.api-reports/api-report-masking.api.md @@ -72,13 +72,13 @@ export type Unmasked = TData extends object ? UnwrapFragmentRefs = string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { +type UnwrapFragmentRefs = TData extends any ? string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { " $fragmentRefs"?: infer FragmentRefs extends object; } ? Prettify<{ [K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs; } & CombineFragmentRefs> : never : TData extends object ? { [K in keyof TData]: UnwrapFragmentRefs; -} : TData; +} : TData : never; // (No @packageDocumentation comment for this package) diff --git a/.api-reports/api-report-react.api.md b/.api-reports/api-report-react.api.md index b72af461a1e..0fe374cb7fc 100644 --- a/.api-reports/api-report-react.api.md +++ b/.api-reports/api-report-react.api.md @@ -2137,13 +2137,13 @@ type Unmasked = TData extends object ? UnwrapFragmentRefs = string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { +type UnwrapFragmentRefs = TData extends any ? string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { " $fragmentRefs"?: infer FragmentRefs extends object; } ? Prettify<{ [K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs; } & CombineFragmentRefs> : never : TData extends object ? { [K in keyof TData]: UnwrapFragmentRefs; -} : TData; +} : TData : never; // @public (undocumented) type UpdateQueries = MutationOptions["updateQueries"]; diff --git a/.api-reports/api-report-react_components.api.md b/.api-reports/api-report-react_components.api.md index 70aa9e0d54d..2c93ea485c2 100644 --- a/.api-reports/api-report-react_components.api.md +++ b/.api-reports/api-report-react_components.api.md @@ -1870,13 +1870,13 @@ type Unmasked = TData extends object ? UnwrapFragmentRefs = string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { +type UnwrapFragmentRefs = TData extends any ? string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { " $fragmentRefs"?: infer FragmentRefs extends object; } ? Prettify<{ [K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs; } & CombineFragmentRefs> : never : TData extends object ? { [K in keyof TData]: UnwrapFragmentRefs; -} : TData; +} : TData : never; // @public (undocumented) type UpdateQueries = MutationOptions["updateQueries"]; diff --git a/.api-reports/api-report-react_context.api.md b/.api-reports/api-report-react_context.api.md index c0bafb7eaba..2703bcf4a31 100644 --- a/.api-reports/api-report-react_context.api.md +++ b/.api-reports/api-report-react_context.api.md @@ -1790,13 +1790,13 @@ type Unmasked = TData extends object ? UnwrapFragmentRefs = string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { +type UnwrapFragmentRefs = TData extends any ? string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { " $fragmentRefs"?: infer FragmentRefs extends object; } ? Prettify<{ [K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs; } & CombineFragmentRefs> : never : TData extends object ? { [K in keyof TData]: UnwrapFragmentRefs; -} : TData; +} : TData : never; // @public (undocumented) type UpdateQueries = MutationOptions["updateQueries"]; diff --git a/.api-reports/api-report-react_hoc.api.md b/.api-reports/api-report-react_hoc.api.md index 20570045c3d..0172940709b 100644 --- a/.api-reports/api-report-react_hoc.api.md +++ b/.api-reports/api-report-react_hoc.api.md @@ -1794,13 +1794,13 @@ type Unmasked = TData extends object ? UnwrapFragmentRefs = string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { +type UnwrapFragmentRefs = TData extends any ? string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { " $fragmentRefs"?: infer FragmentRefs extends object; } ? Prettify<{ [K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs; } & CombineFragmentRefs> : never : TData extends object ? { [K in keyof TData]: UnwrapFragmentRefs; -} : TData; +} : TData : never; // @public (undocumented) type UpdateQueries = MutationOptions["updateQueries"]; diff --git a/.api-reports/api-report-react_hooks.api.md b/.api-reports/api-report-react_hooks.api.md index 3fca18975da..beae7ff4e42 100644 --- a/.api-reports/api-report-react_hooks.api.md +++ b/.api-reports/api-report-react_hooks.api.md @@ -1960,13 +1960,13 @@ type Unmasked = TData extends object ? UnwrapFragmentRefs = string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { +type UnwrapFragmentRefs = TData extends any ? string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { " $fragmentRefs"?: infer FragmentRefs extends object; } ? Prettify<{ [K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs; } & CombineFragmentRefs> : never : TData extends object ? { [K in keyof TData]: UnwrapFragmentRefs; -} : TData; +} : TData : never; // @public (undocumented) type UpdateQueries = MutationOptions["updateQueries"]; diff --git a/.api-reports/api-report-react_internal.api.md b/.api-reports/api-report-react_internal.api.md index 9505c29f83b..4038852a7e9 100644 --- a/.api-reports/api-report-react_internal.api.md +++ b/.api-reports/api-report-react_internal.api.md @@ -2012,13 +2012,13 @@ type Unmasked = TData extends object ? UnwrapFragmentRefs = string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { +type UnwrapFragmentRefs = TData extends any ? string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { " $fragmentRefs"?: infer FragmentRefs extends object; } ? Prettify<{ [K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs; } & CombineFragmentRefs> : never : TData extends object ? { [K in keyof TData]: UnwrapFragmentRefs; -} : TData; +} : TData : never; // @public (undocumented) export function unwrapQueryRef(queryRef: WrappedQueryRef): InternalQueryReference; diff --git a/.api-reports/api-report-react_ssr.api.md b/.api-reports/api-report-react_ssr.api.md index c93a3387a0a..61b56d41e68 100644 --- a/.api-reports/api-report-react_ssr.api.md +++ b/.api-reports/api-report-react_ssr.api.md @@ -1775,13 +1775,13 @@ type Unmasked = TData extends object ? UnwrapFragmentRefs = string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { +type UnwrapFragmentRefs = TData extends any ? string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { " $fragmentRefs"?: infer FragmentRefs extends object; } ? Prettify<{ [K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs; } & CombineFragmentRefs> : never : TData extends object ? { [K in keyof TData]: UnwrapFragmentRefs; -} : TData; +} : TData : never; // @public (undocumented) type UpdateQueries = MutationOptions["updateQueries"]; diff --git a/.api-reports/api-report-testing.api.md b/.api-reports/api-report-testing.api.md index 1ba1d98f2b2..d6e7d4209df 100644 --- a/.api-reports/api-report-testing.api.md +++ b/.api-reports/api-report-testing.api.md @@ -1826,13 +1826,13 @@ type Unmasked = TData extends object ? UnwrapFragmentRefs = string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { +type UnwrapFragmentRefs = TData extends any ? string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { " $fragmentRefs"?: infer FragmentRefs extends object; } ? Prettify<{ [K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs; } & CombineFragmentRefs> : never : TData extends object ? { [K in keyof TData]: UnwrapFragmentRefs; -} : TData; +} : TData : never; // @public (undocumented) type UpdateQueries = MutationOptions["updateQueries"]; diff --git a/.api-reports/api-report-testing_core.api.md b/.api-reports/api-report-testing_core.api.md index 9695c5890d4..d04ffbb1842 100644 --- a/.api-reports/api-report-testing_core.api.md +++ b/.api-reports/api-report-testing_core.api.md @@ -1783,13 +1783,13 @@ type Unmasked = TData extends object ? UnwrapFragmentRefs = string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { +type UnwrapFragmentRefs = TData extends any ? string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { " $fragmentRefs"?: infer FragmentRefs extends object; } ? Prettify<{ [K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs; } & CombineFragmentRefs> : never : TData extends object ? { [K in keyof TData]: UnwrapFragmentRefs; -} : TData; +} : TData : never; // @public (undocumented) type UpdateQueries = MutationOptions["updateQueries"]; diff --git a/.api-reports/api-report-utilities.api.md b/.api-reports/api-report-utilities.api.md index dd76c0acc5d..3ec50c2993b 100644 --- a/.api-reports/api-report-utilities.api.md +++ b/.api-reports/api-report-utilities.api.md @@ -2701,13 +2701,13 @@ type Unmasked = TData extends object ? UnwrapFragmentRefs = string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { +type UnwrapFragmentRefs = TData extends any ? string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { " $fragmentRefs"?: infer FragmentRefs extends object; } ? Prettify<{ [K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs; } & CombineFragmentRefs> : never : TData extends object ? { [K in keyof TData]: UnwrapFragmentRefs; -} : TData; +} : TData : never; // @public (undocumented) type UpdateQueries = MutationOptions["updateQueries"]; diff --git a/.api-reports/api-report.api.md b/.api-reports/api-report.api.md index 5058c56771a..ee8dc636743 100644 --- a/.api-reports/api-report.api.md +++ b/.api-reports/api-report.api.md @@ -2806,13 +2806,13 @@ export type Unmasked = TData extends object ? UnwrapFragmentRefs = string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { +type UnwrapFragmentRefs = TData extends any ? string extends keyof NonNullable ? TData : " $fragmentRefs" extends keyof NonNullable ? TData extends { " $fragmentRefs"?: infer FragmentRefs extends object; } ? Prettify<{ [K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs; } & CombineFragmentRefs> : never : TData extends object ? { [K in keyof TData]: UnwrapFragmentRefs; -} : TData; +} : TData : never; // @public (undocumented) type UpdateQueries = MutationOptions["updateQueries"]; diff --git a/.changeset/small-bears-confess.md b/.changeset/small-bears-confess.md new file mode 100644 index 00000000000..d062a652746 --- /dev/null +++ b/.changeset/small-bears-confess.md @@ -0,0 +1,5 @@ +--- +"@apollo/client": patch +--- + +Ensure `null` is retained in nullable types when unmasking a type with the `Unmasked` helper type. diff --git a/src/masking/__tests__/types.test.ts b/src/masking/__tests__/types.test.ts index 7eb07b96794..a016a70fc4d 100644 --- a/src/masking/__tests__/types.test.ts +++ b/src/masking/__tests__/types.test.ts @@ -96,6 +96,73 @@ describe.skip("Unmasked", () => { }>(); }); + test("unmasks deeply nested nullable fragments", () => { + type UserFieldsFragment = { + __typename: "User"; + id: number; + age: number; + career: + | ({ + __typename: "Job"; + id: string; + title: string; + } & { " $fragmentRefs"?: { JobFieldsFragment: JobFieldsFragment } }) + | null; + jobs: Array< + | ({ + __typename: "Job"; + id: string; + title: string; + } & { " $fragmentRefs"?: { JobFieldsFragment: JobFieldsFragment } }) + | null + >; + } & { " $fragmentName"?: "UserFieldsFragment" } & { + " $fragmentRefs"?: { + NameFieldsFragment: NameFieldsFragment; + }; + }; + + type NameFieldsFragment = { + __typename: "User"; + firstName: string; + lastName: string; + } & { " $fragmentName"?: "NameFieldsFragment" }; + + type JobFieldsFragment = { + __typename: "Job"; + job: string; + } & { " $fragmentName"?: "JobFieldsFragment" } & { + " $fragmentRefs"?: { CareerFieldsFragment: CareerFieldsFragment }; + }; + + type CareerFieldsFragment = { + __typename: "Job"; + position: string; + } & { " $fragmentName"?: "CareerFieldsFragment" }; + + expectTypeOf>().toEqualTypeOf<{ + __typename: "User"; + id: number; + age: number; + firstName: string; + lastName: string; + career: { + __typename: "Job"; + id: string; + title: string; + job: string; + position: string; + } | null; + jobs: Array<{ + __typename: "Job"; + id: string; + title: string; + job: string; + position: string; + } | null>; + }>(); + }); + test("unmasks DeepPartial types", () => { type UserFieldsFragment = { __typename: "User"; diff --git a/src/masking/internal/types.ts b/src/masking/internal/types.ts index b92d1b03cca..02d8fdc99b6 100644 --- a/src/masking/internal/types.ts +++ b/src/masking/internal/types.ts @@ -1,19 +1,22 @@ import type { Prettify, UnionToIntersection } from "../../utilities/index.js"; export type UnwrapFragmentRefs = - // Leave TData alone if it is Record and not a specific shape - string extends keyof NonNullable ? TData - : " $fragmentRefs" extends keyof NonNullable ? - TData extends { " $fragmentRefs"?: infer FragmentRefs extends object } ? - Prettify< - { - [K in keyof TData as K extends " $fragmentRefs" ? never - : K]: UnwrapFragmentRefs; - } & CombineFragmentRefs - > - : never - : TData extends object ? { [K in keyof TData]: UnwrapFragmentRefs } - : TData; + TData extends any ? + // Leave TData alone if it is Record and not a specific shape + string extends keyof NonNullable ? TData + : " $fragmentRefs" extends keyof NonNullable ? + TData extends { " $fragmentRefs"?: infer FragmentRefs extends object } ? + Prettify< + { + [K in keyof TData as K extends " $fragmentRefs" ? never + : K]: UnwrapFragmentRefs; + } & CombineFragmentRefs + > + : never + : TData extends object ? + { [K in keyof TData]: UnwrapFragmentRefs } + : TData + : never; type CombineFragmentRefs> = UnionToIntersection<