Skip to content

Commit

Permalink
[Data masking] Ensure null is retained when unmasking nullable obje…
Browse files Browse the repository at this point in the history
…cts (#12133)
  • Loading branch information
jerelmiller authored Nov 15, 2024
1 parent 21c3f08 commit a6ece37
Show file tree
Hide file tree
Showing 17 changed files with 116 additions and 41 deletions.
4 changes: 2 additions & 2 deletions .api-reports/api-report-cache.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1044,13 +1044,13 @@ type Unmasked<TData> = TData extends object ? UnwrapFragmentRefs<RemoveMaskedMar
// Warning: (ae-forgotten-export) The symbol "CombineFragmentRefs" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
type UnwrapFragmentRefs<TData> = string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
type UnwrapFragmentRefs<TData> = TData extends any ? string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
" $fragmentRefs"?: infer FragmentRefs extends object;
} ? Prettify<{
[K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs<TData[K]>;
} & CombineFragmentRefs<FragmentRefs>> : never : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
} : TData;
} : TData : never;

// @public
export interface WatchFragmentOptions<TData, TVars> {
Expand Down
4 changes: 2 additions & 2 deletions .api-reports/api-report-core.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2341,13 +2341,13 @@ export type Unmasked<TData> = TData extends object ? UnwrapFragmentRefs<RemoveMa
// Warning: (ae-forgotten-export) The symbol "CombineFragmentRefs" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
type UnwrapFragmentRefs<TData> = string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
type UnwrapFragmentRefs<TData> = TData extends any ? string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
" $fragmentRefs"?: infer FragmentRefs extends object;
} ? Prettify<{
[K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs<TData[K]>;
} & CombineFragmentRefs<FragmentRefs>> : never : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
} : TData;
} : TData : never;

// @public (undocumented)
type UpdateQueries<TData> = MutationOptions<TData, any, any>["updateQueries"];
Expand Down
4 changes: 2 additions & 2 deletions .api-reports/api-report-masking.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,13 @@ export type Unmasked<TData> = TData extends object ? UnwrapFragmentRefs<RemoveMa
// Warning: (ae-forgotten-export) The symbol "CombineFragmentRefs" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
type UnwrapFragmentRefs<TData> = string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
type UnwrapFragmentRefs<TData> = TData extends any ? string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
" $fragmentRefs"?: infer FragmentRefs extends object;
} ? Prettify<{
[K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs<TData[K]>;
} & CombineFragmentRefs<FragmentRefs>> : never : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
} : TData;
} : TData : never;

// (No @packageDocumentation comment for this package)

Expand Down
4 changes: 2 additions & 2 deletions .api-reports/api-report-react.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2137,13 +2137,13 @@ type Unmasked<TData> = TData extends object ? UnwrapFragmentRefs<RemoveMaskedMar
// Warning: (ae-forgotten-export) The symbol "CombineFragmentRefs" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
type UnwrapFragmentRefs<TData> = string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
type UnwrapFragmentRefs<TData> = TData extends any ? string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
" $fragmentRefs"?: infer FragmentRefs extends object;
} ? Prettify<{
[K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs<TData[K]>;
} & CombineFragmentRefs<FragmentRefs>> : never : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
} : TData;
} : TData : never;

// @public (undocumented)
type UpdateQueries<TData> = MutationOptions<TData, any, any>["updateQueries"];
Expand Down
4 changes: 2 additions & 2 deletions .api-reports/api-report-react_components.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1870,13 +1870,13 @@ type Unmasked<TData> = TData extends object ? UnwrapFragmentRefs<RemoveMaskedMar
// Warning: (ae-forgotten-export) The symbol "CombineFragmentRefs" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
type UnwrapFragmentRefs<TData> = string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
type UnwrapFragmentRefs<TData> = TData extends any ? string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
" $fragmentRefs"?: infer FragmentRefs extends object;
} ? Prettify<{
[K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs<TData[K]>;
} & CombineFragmentRefs<FragmentRefs>> : never : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
} : TData;
} : TData : never;

// @public (undocumented)
type UpdateQueries<TData> = MutationOptions<TData, any, any>["updateQueries"];
Expand Down
4 changes: 2 additions & 2 deletions .api-reports/api-report-react_context.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1790,13 +1790,13 @@ type Unmasked<TData> = TData extends object ? UnwrapFragmentRefs<RemoveMaskedMar
// Warning: (ae-forgotten-export) The symbol "CombineFragmentRefs" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
type UnwrapFragmentRefs<TData> = string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
type UnwrapFragmentRefs<TData> = TData extends any ? string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
" $fragmentRefs"?: infer FragmentRefs extends object;
} ? Prettify<{
[K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs<TData[K]>;
} & CombineFragmentRefs<FragmentRefs>> : never : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
} : TData;
} : TData : never;

// @public (undocumented)
type UpdateQueries<TData> = MutationOptions<TData, any, any>["updateQueries"];
Expand Down
4 changes: 2 additions & 2 deletions .api-reports/api-report-react_hoc.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1794,13 +1794,13 @@ type Unmasked<TData> = TData extends object ? UnwrapFragmentRefs<RemoveMaskedMar
// Warning: (ae-forgotten-export) The symbol "CombineFragmentRefs" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
type UnwrapFragmentRefs<TData> = string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
type UnwrapFragmentRefs<TData> = TData extends any ? string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
" $fragmentRefs"?: infer FragmentRefs extends object;
} ? Prettify<{
[K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs<TData[K]>;
} & CombineFragmentRefs<FragmentRefs>> : never : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
} : TData;
} : TData : never;

// @public (undocumented)
type UpdateQueries<TData> = MutationOptions<TData, any, any>["updateQueries"];
Expand Down
4 changes: 2 additions & 2 deletions .api-reports/api-report-react_hooks.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1960,13 +1960,13 @@ type Unmasked<TData> = TData extends object ? UnwrapFragmentRefs<RemoveMaskedMar
// Warning: (ae-forgotten-export) The symbol "CombineFragmentRefs" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
type UnwrapFragmentRefs<TData> = string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
type UnwrapFragmentRefs<TData> = TData extends any ? string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
" $fragmentRefs"?: infer FragmentRefs extends object;
} ? Prettify<{
[K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs<TData[K]>;
} & CombineFragmentRefs<FragmentRefs>> : never : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
} : TData;
} : TData : never;

// @public (undocumented)
type UpdateQueries<TData> = MutationOptions<TData, any, any>["updateQueries"];
Expand Down
4 changes: 2 additions & 2 deletions .api-reports/api-report-react_internal.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2012,13 +2012,13 @@ type Unmasked<TData> = TData extends object ? UnwrapFragmentRefs<RemoveMaskedMar
// Warning: (ae-forgotten-export) The symbol "CombineFragmentRefs" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
type UnwrapFragmentRefs<TData> = string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
type UnwrapFragmentRefs<TData> = TData extends any ? string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
" $fragmentRefs"?: infer FragmentRefs extends object;
} ? Prettify<{
[K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs<TData[K]>;
} & CombineFragmentRefs<FragmentRefs>> : never : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
} : TData;
} : TData : never;

// @public (undocumented)
export function unwrapQueryRef<TData>(queryRef: WrappedQueryRef<TData>): InternalQueryReference<TData>;
Expand Down
4 changes: 2 additions & 2 deletions .api-reports/api-report-react_ssr.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1775,13 +1775,13 @@ type Unmasked<TData> = TData extends object ? UnwrapFragmentRefs<RemoveMaskedMar
// Warning: (ae-forgotten-export) The symbol "CombineFragmentRefs" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
type UnwrapFragmentRefs<TData> = string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
type UnwrapFragmentRefs<TData> = TData extends any ? string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
" $fragmentRefs"?: infer FragmentRefs extends object;
} ? Prettify<{
[K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs<TData[K]>;
} & CombineFragmentRefs<FragmentRefs>> : never : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
} : TData;
} : TData : never;

// @public (undocumented)
type UpdateQueries<TData> = MutationOptions<TData, any, any>["updateQueries"];
Expand Down
4 changes: 2 additions & 2 deletions .api-reports/api-report-testing.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1826,13 +1826,13 @@ type Unmasked<TData> = TData extends object ? UnwrapFragmentRefs<RemoveMaskedMar
// Warning: (ae-forgotten-export) The symbol "CombineFragmentRefs" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
type UnwrapFragmentRefs<TData> = string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
type UnwrapFragmentRefs<TData> = TData extends any ? string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
" $fragmentRefs"?: infer FragmentRefs extends object;
} ? Prettify<{
[K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs<TData[K]>;
} & CombineFragmentRefs<FragmentRefs>> : never : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
} : TData;
} : TData : never;

// @public (undocumented)
type UpdateQueries<TData> = MutationOptions<TData, any, any>["updateQueries"];
Expand Down
4 changes: 2 additions & 2 deletions .api-reports/api-report-testing_core.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1783,13 +1783,13 @@ type Unmasked<TData> = TData extends object ? UnwrapFragmentRefs<RemoveMaskedMar
// Warning: (ae-forgotten-export) The symbol "CombineFragmentRefs" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
type UnwrapFragmentRefs<TData> = string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
type UnwrapFragmentRefs<TData> = TData extends any ? string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
" $fragmentRefs"?: infer FragmentRefs extends object;
} ? Prettify<{
[K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs<TData[K]>;
} & CombineFragmentRefs<FragmentRefs>> : never : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
} : TData;
} : TData : never;

// @public (undocumented)
type UpdateQueries<TData> = MutationOptions<TData, any, any>["updateQueries"];
Expand Down
4 changes: 2 additions & 2 deletions .api-reports/api-report-utilities.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2701,13 +2701,13 @@ type Unmasked<TData> = TData extends object ? UnwrapFragmentRefs<RemoveMaskedMar
// Warning: (ae-forgotten-export) The symbol "CombineFragmentRefs" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
type UnwrapFragmentRefs<TData> = string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
type UnwrapFragmentRefs<TData> = TData extends any ? string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
" $fragmentRefs"?: infer FragmentRefs extends object;
} ? Prettify<{
[K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs<TData[K]>;
} & CombineFragmentRefs<FragmentRefs>> : never : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
} : TData;
} : TData : never;

// @public (undocumented)
type UpdateQueries<TData> = MutationOptions<TData, any, any>["updateQueries"];
Expand Down
4 changes: 2 additions & 2 deletions .api-reports/api-report.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2806,13 +2806,13 @@ export type Unmasked<TData> = TData extends object ? UnwrapFragmentRefs<RemoveMa
// Warning: (ae-forgotten-export) The symbol "CombineFragmentRefs" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
type UnwrapFragmentRefs<TData> = string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
type UnwrapFragmentRefs<TData> = TData extends any ? string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
" $fragmentRefs"?: infer FragmentRefs extends object;
} ? Prettify<{
[K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs<TData[K]>;
} & CombineFragmentRefs<FragmentRefs>> : never : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
} : TData;
} : TData : never;

// @public (undocumented)
type UpdateQueries<TData> = MutationOptions<TData, any, any>["updateQueries"];
Expand Down
5 changes: 5 additions & 0 deletions .changeset/small-bears-confess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@apollo/client": patch
---

Ensure `null` is retained in nullable types when unmasking a type with the `Unmasked` helper type.
67 changes: 67 additions & 0 deletions src/masking/__tests__/types.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Unmasked<UserFieldsFragment>>().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";
Expand Down
29 changes: 16 additions & 13 deletions src/masking/internal/types.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import type { Prettify, UnionToIntersection } from "../../utilities/index.js";

export type UnwrapFragmentRefs<TData> =
// Leave TData alone if it is Record<string, any> and not a specific shape
string extends keyof NonNullable<TData> ? TData
: " $fragmentRefs" extends keyof NonNullable<TData> ?
TData extends { " $fragmentRefs"?: infer FragmentRefs extends object } ?
Prettify<
{
[K in keyof TData as K extends " $fragmentRefs" ? never
: K]: UnwrapFragmentRefs<TData[K]>;
} & CombineFragmentRefs<FragmentRefs>
>
: never
: TData extends object ? { [K in keyof TData]: UnwrapFragmentRefs<TData[K]> }
: TData;
TData extends any ?
// Leave TData alone if it is Record<string, any> and not a specific shape
string extends keyof NonNullable<TData> ? TData
: " $fragmentRefs" extends keyof NonNullable<TData> ?
TData extends { " $fragmentRefs"?: infer FragmentRefs extends object } ?
Prettify<
{
[K in keyof TData as K extends " $fragmentRefs" ? never
: K]: UnwrapFragmentRefs<TData[K]>;
} & CombineFragmentRefs<FragmentRefs>
>
: never
: TData extends object ?
{ [K in keyof TData]: UnwrapFragmentRefs<TData[K]> }
: TData
: never;

type CombineFragmentRefs<FragmentRefs extends Record<string, any>> =
UnionToIntersection<
Expand Down

0 comments on commit a6ece37

Please sign in to comment.