Skip to content

Commit

Permalink
Handle tagged/branded primitive types when used with Unmasked (#12270)
Browse files Browse the repository at this point in the history
  • Loading branch information
jerelmiller authored Jan 14, 2025
1 parent d57429d commit 3601246
Show file tree
Hide file tree
Showing 18 changed files with 62 additions and 19 deletions.
2 changes: 1 addition & 1 deletion .api-reports/api-report-cache.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1120,7 +1120,7 @@ type unionToIntersection<T> = (T extends unknown ? (x: T) => unknown : never) ex
type Unmasked<TData> = true extends IsAny<TData> ? TData : TData extends object ? true extends ContainsFragmentsRefs<TData> ? UnwrapFragmentRefs<RemoveMaskedMarker<RemoveFragmentName<TData>>> : TData : TData;

// @public (undocumented)
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? TData extends Primitive ? TData : string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
" $fragmentRefs"?: infer FragmentRefs;
} ? UnwrapFragmentRefs<CombineIntersection<Omit<TData, " $fragmentRefs"> | RemoveFragmentName<NonNullable<NonNullable<FragmentRefs>[keyof NonNullable<FragmentRefs>]>>>> : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
Expand Down
2 changes: 1 addition & 1 deletion .api-reports/api-report-core.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2417,7 +2417,7 @@ type unionToIntersection<T> = (T extends unknown ? (x: T) => unknown : never) ex
export type Unmasked<TData> = true extends IsAny<TData> ? TData : TData extends object ? true extends ContainsFragmentsRefs<TData> ? UnwrapFragmentRefs<RemoveMaskedMarker<RemoveFragmentName<TData>>> : TData : TData;

// @public (undocumented)
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? TData extends Primitive ? TData : string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
" $fragmentRefs"?: infer FragmentRefs;
} ? UnwrapFragmentRefs<CombineIntersection<Omit<TData, " $fragmentRefs"> | RemoveFragmentName<NonNullable<NonNullable<FragmentRefs>[keyof NonNullable<FragmentRefs>]>>>> : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
Expand Down
2 changes: 1 addition & 1 deletion .api-reports/api-report-masking.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,7 @@ type unionToIntersection<T> = (T extends unknown ? (x: T) => unknown : never) ex
export type Unmasked<TData> = true extends IsAny<TData> ? TData : TData extends object ? true extends ContainsFragmentsRefs<TData> ? UnwrapFragmentRefs<RemoveMaskedMarker<RemoveFragmentName<TData>>> : TData : TData;

// @public (undocumented)
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? TData extends Primitive ? TData : string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
" $fragmentRefs"?: infer FragmentRefs;
} ? UnwrapFragmentRefs<CombineIntersection<Omit<TData, " $fragmentRefs"> | RemoveFragmentName<NonNullable<NonNullable<FragmentRefs>[keyof NonNullable<FragmentRefs>]>>>> : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
Expand Down
2 changes: 1 addition & 1 deletion .api-reports/api-report-react.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2213,7 +2213,7 @@ type unionToIntersection<T> = (T extends unknown ? (x: T) => unknown : never) ex
type Unmasked<TData> = true extends IsAny<TData> ? TData : TData extends object ? true extends ContainsFragmentsRefs<TData> ? UnwrapFragmentRefs<RemoveMaskedMarker<RemoveFragmentName<TData>>> : TData : TData;

// @public (undocumented)
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? TData extends Primitive ? TData : string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
" $fragmentRefs"?: infer FragmentRefs;
} ? UnwrapFragmentRefs<CombineIntersection<Omit<TData, " $fragmentRefs"> | RemoveFragmentName<NonNullable<NonNullable<FragmentRefs>[keyof NonNullable<FragmentRefs>]>>>> : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
Expand Down
2 changes: 1 addition & 1 deletion .api-reports/api-report-react_components.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1946,7 +1946,7 @@ type unionToIntersection<T> = (T extends unknown ? (x: T) => unknown : never) ex
type Unmasked<TData> = true extends IsAny<TData> ? TData : TData extends object ? true extends ContainsFragmentsRefs<TData> ? UnwrapFragmentRefs<RemoveMaskedMarker<RemoveFragmentName<TData>>> : TData : TData;

// @public (undocumented)
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? TData extends Primitive ? TData : string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
" $fragmentRefs"?: infer FragmentRefs;
} ? UnwrapFragmentRefs<CombineIntersection<Omit<TData, " $fragmentRefs"> | RemoveFragmentName<NonNullable<NonNullable<FragmentRefs>[keyof NonNullable<FragmentRefs>]>>>> : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
Expand Down
2 changes: 1 addition & 1 deletion .api-reports/api-report-react_context.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1866,7 +1866,7 @@ type unionToIntersection<T> = (T extends unknown ? (x: T) => unknown : never) ex
type Unmasked<TData> = true extends IsAny<TData> ? TData : TData extends object ? true extends ContainsFragmentsRefs<TData> ? UnwrapFragmentRefs<RemoveMaskedMarker<RemoveFragmentName<TData>>> : TData : TData;

// @public (undocumented)
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? TData extends Primitive ? TData : string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
" $fragmentRefs"?: infer FragmentRefs;
} ? UnwrapFragmentRefs<CombineIntersection<Omit<TData, " $fragmentRefs"> | RemoveFragmentName<NonNullable<NonNullable<FragmentRefs>[keyof NonNullable<FragmentRefs>]>>>> : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
Expand Down
2 changes: 1 addition & 1 deletion .api-reports/api-report-react_hoc.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1870,7 +1870,7 @@ type unionToIntersection<T> = (T extends unknown ? (x: T) => unknown : never) ex
type Unmasked<TData> = true extends IsAny<TData> ? TData : TData extends object ? true extends ContainsFragmentsRefs<TData> ? UnwrapFragmentRefs<RemoveMaskedMarker<RemoveFragmentName<TData>>> : TData : TData;

// @public (undocumented)
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? TData extends Primitive ? TData : string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
" $fragmentRefs"?: infer FragmentRefs;
} ? UnwrapFragmentRefs<CombineIntersection<Omit<TData, " $fragmentRefs"> | RemoveFragmentName<NonNullable<NonNullable<FragmentRefs>[keyof NonNullable<FragmentRefs>]>>>> : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
Expand Down
2 changes: 1 addition & 1 deletion .api-reports/api-report-react_hooks.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2036,7 +2036,7 @@ type unionToIntersection<T> = (T extends unknown ? (x: T) => unknown : never) ex
type Unmasked<TData> = true extends IsAny<TData> ? TData : TData extends object ? true extends ContainsFragmentsRefs<TData> ? UnwrapFragmentRefs<RemoveMaskedMarker<RemoveFragmentName<TData>>> : TData : TData;

// @public (undocumented)
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? TData extends Primitive ? TData : string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
" $fragmentRefs"?: infer FragmentRefs;
} ? UnwrapFragmentRefs<CombineIntersection<Omit<TData, " $fragmentRefs"> | RemoveFragmentName<NonNullable<NonNullable<FragmentRefs>[keyof NonNullable<FragmentRefs>]>>>> : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
Expand Down
2 changes: 1 addition & 1 deletion .api-reports/api-report-react_internal.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2088,7 +2088,7 @@ type unionToIntersection<T> = (T extends unknown ? (x: T) => unknown : never) ex
type Unmasked<TData> = true extends IsAny<TData> ? TData : TData extends object ? true extends ContainsFragmentsRefs<TData> ? UnwrapFragmentRefs<RemoveMaskedMarker<RemoveFragmentName<TData>>> : TData : TData;

// @public (undocumented)
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? TData extends Primitive ? TData : string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
" $fragmentRefs"?: infer FragmentRefs;
} ? UnwrapFragmentRefs<CombineIntersection<Omit<TData, " $fragmentRefs"> | RemoveFragmentName<NonNullable<NonNullable<FragmentRefs>[keyof NonNullable<FragmentRefs>]>>>> : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
Expand Down
2 changes: 1 addition & 1 deletion .api-reports/api-report-react_ssr.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1851,7 +1851,7 @@ type unionToIntersection<T> = (T extends unknown ? (x: T) => unknown : never) ex
type Unmasked<TData> = true extends IsAny<TData> ? TData : TData extends object ? true extends ContainsFragmentsRefs<TData> ? UnwrapFragmentRefs<RemoveMaskedMarker<RemoveFragmentName<TData>>> : TData : TData;

// @public (undocumented)
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? TData extends Primitive ? TData : string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
" $fragmentRefs"?: infer FragmentRefs;
} ? UnwrapFragmentRefs<CombineIntersection<Omit<TData, " $fragmentRefs"> | RemoveFragmentName<NonNullable<NonNullable<FragmentRefs>[keyof NonNullable<FragmentRefs>]>>>> : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
Expand Down
2 changes: 1 addition & 1 deletion .api-reports/api-report-testing.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1902,7 +1902,7 @@ type unionToIntersection<T> = (T extends unknown ? (x: T) => unknown : never) ex
type Unmasked<TData> = true extends IsAny<TData> ? TData : TData extends object ? true extends ContainsFragmentsRefs<TData> ? UnwrapFragmentRefs<RemoveMaskedMarker<RemoveFragmentName<TData>>> : TData : TData;

// @public (undocumented)
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? TData extends Primitive ? TData : string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
" $fragmentRefs"?: infer FragmentRefs;
} ? UnwrapFragmentRefs<CombineIntersection<Omit<TData, " $fragmentRefs"> | RemoveFragmentName<NonNullable<NonNullable<FragmentRefs>[keyof NonNullable<FragmentRefs>]>>>> : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
Expand Down
2 changes: 1 addition & 1 deletion .api-reports/api-report-testing_core.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1859,7 +1859,7 @@ type unionToIntersection<T> = (T extends unknown ? (x: T) => unknown : never) ex
type Unmasked<TData> = true extends IsAny<TData> ? TData : TData extends object ? true extends ContainsFragmentsRefs<TData> ? UnwrapFragmentRefs<RemoveMaskedMarker<RemoveFragmentName<TData>>> : TData : TData;

// @public (undocumented)
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? TData extends Primitive ? TData : string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
" $fragmentRefs"?: infer FragmentRefs;
} ? UnwrapFragmentRefs<CombineIntersection<Omit<TData, " $fragmentRefs"> | RemoveFragmentName<NonNullable<NonNullable<FragmentRefs>[keyof NonNullable<FragmentRefs>]>>>> : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
Expand Down
6 changes: 2 additions & 4 deletions .api-reports/api-report-utilities.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -729,8 +729,6 @@ type DeepOmitArray<T extends any[], K> = {
[P in keyof T]: DeepOmit<T[P], K>;
};

// Warning: (ae-forgotten-export) The symbol "Primitive" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
type DeepOmitPrimitive = Primitive | Function;

Expand Down Expand Up @@ -2142,7 +2140,7 @@ export type Prettify<T> = {
export function preventUnhandledRejection<T>(promise: Promise<T>): Promise<T>;

// @public (undocumented)
type Primitive = null | undefined | string | number | boolean | symbol | bigint;
export type Primitive = null | undefined | string | number | boolean | symbol | bigint;

// @public (undocumented)
const print_2: ((ast: ASTNode) => string) & {
Expand Down Expand Up @@ -2780,7 +2778,7 @@ type UnionToIntersection_2<U> = (U extends any ? (k: U) => void : never) extends
type Unmasked<TData> = true extends IsAny<TData> ? TData : TData extends object ? true extends ContainsFragmentsRefs<TData> ? UnwrapFragmentRefs<RemoveMaskedMarker<RemoveFragmentName<TData>>> : TData : TData;

// @public (undocumented)
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? TData extends Primitive ? TData : string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
" $fragmentRefs"?: infer FragmentRefs;
} ? UnwrapFragmentRefs<CombineIntersection<Omit<TData, " $fragmentRefs"> | RemoveFragmentName<NonNullable<NonNullable<FragmentRefs>[keyof NonNullable<FragmentRefs>]>>>> : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
Expand Down
2 changes: 1 addition & 1 deletion .api-reports/api-report.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2882,7 +2882,7 @@ type unionToIntersection<T> = (T extends unknown ? (x: T) => unknown : never) ex
export type Unmasked<TData> = true extends IsAny<TData> ? TData : TData extends object ? true extends ContainsFragmentsRefs<TData> ? UnwrapFragmentRefs<RemoveMaskedMarker<RemoveFragmentName<TData>>> : TData : TData;

// @public (undocumented)
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
type UnwrapFragmentRefs<TData> = true extends IsAny<TData> ? TData : TData extends any ? TData extends Primitive ? TData : string extends keyof TData ? TData : keyof TData extends never ? TData : TData extends {
" $fragmentRefs"?: infer FragmentRefs;
} ? UnwrapFragmentRefs<CombineIntersection<Omit<TData, " $fragmentRefs"> | RemoveFragmentName<NonNullable<NonNullable<FragmentRefs>[keyof NonNullable<FragmentRefs>]>>>> : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
Expand Down
5 changes: 5 additions & 0 deletions .changeset/tiny-ants-bow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@apollo/client": patch
---

Fix handling of tagged/branded primitive types when used as scalar values with `Unmasked`.
33 changes: 33 additions & 0 deletions src/masking/__benches__/types.bench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -627,3 +627,36 @@ test("MaybeMasked can be called with a generic if `mode` is not set to `unmask`"
withGenericResult({} as any);
withGenericDocument({} as any);
});

test("Unmasked handles branded primitive types", (prefix) => {
type Branded<T, Name extends string> = T & { __branded?: Name };
type UUID = Branded<string, "UUID">;
type Source = {
__typename: "Foo";
id: UUID;
name: string;
} & { " $fragmentName"?: "UserFieldsFragment" } & {
" $fragmentRefs"?: {
FooFragment: FooFragment;
};
};
type FooFragment = {
__typename: "Foo";
age: number;
} & { " $fragmentName"?: "UserFieldsFragment" };

bench(prefix + "instantiations", () => {
return {} as Unmasked<Source>;
}).types([5, "instantiations"]);

bench(prefix + "functionality", () => {
const x = {} as Unmasked<Source>;

expectTypeOf(x).branded.toEqualTypeOf<{
__typename: "Foo";
id: UUID;
name: string;
age: number;
}>();
});
});
10 changes: 8 additions & 2 deletions src/masking/internal/types.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import type { Prettify, RemoveIndexSignature } from "../../utilities/index.js";
import type {
Prettify,
Primitive,
RemoveIndexSignature,
} from "../../utilities/index.js";

export type IsAny<T> = 0 extends 1 & T ? true : false;

export type UnwrapFragmentRefs<TData> =
true extends IsAny<TData> ? TData
: TData extends any ?
// Leave TData alone if it is Record<string, any> and not a specific shape
// Ensure tagged/branded types are left alone (i.e. type UUID = string & { ... })
TData extends Primitive ? TData
: // Leave TData alone if it is Record<string, any> and not a specific shape
string extends keyof TData ? TData
: // short-circuit on empty object
keyof TData extends never ? TData
Expand Down
1 change: 1 addition & 0 deletions src/utilities/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ export type { DeepOmit } from "./types/DeepOmit.js";
export type { DeepPartial } from "./types/DeepPartial.js";
export type { OnlyRequiredProperties } from "./types/OnlyRequiredProperties.js";
export type { Prettify } from "./types/Prettify.js";
export type { Primitive } from "./types/Primitive.js";
export type { UnionToIntersection } from "./types/UnionToIntersection.js";
export type { NoInfer } from "./types/NoInfer.js";
export type { RemoveIndexSignature } from "./types/RemoveIndexSignature.js";
Expand Down

0 comments on commit 3601246

Please sign in to comment.