Skip to content

Commit d71f2e8

Browse files
authored
Require variables option in useSuspenseFragment when there are required variables (#12363)
1 parent 27e1532 commit d71f2e8

File tree

9 files changed

+246
-69
lines changed

9 files changed

+246
-69
lines changed

.api-reports/api-report-react.api.md

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1547,8 +1547,6 @@ export interface PreloadQueryFunction {
15471547
<TData = unknown, TVariables extends OperationVariables = OperationVariables>(query: DocumentNode | TypedDocumentNode<TData, TVariables>, ...[options]: PreloadQueryOptionsArg<NoInfer_2<TVariables>>): PreloadedQueryRef<TData, TVariables>;
15481548
}
15491549

1550-
// Warning: (ae-forgotten-export) The symbol "VariablesOption" needs to be exported by the entry point index.d.ts
1551-
//
15521550
// @public (undocumented)
15531551
export type PreloadQueryOptions<TVariables extends OperationVariables = OperationVariables> = {
15541552
canonizeResults?: boolean;
@@ -2473,13 +2471,13 @@ export function useSuspenseFragment<TData, TVariables extends OperationVariables
24732471
export function useSuspenseFragment<TData, TVariables extends OperationVariables = OperationVariables>(options: UseSuspenseFragmentOptions<TData, TVariables>): UseSuspenseFragmentResult<TData>;
24742472

24752473
// @public (undocumented)
2476-
interface UseSuspenseFragmentOptions<TData, TVars> extends Omit<Cache_2.DiffOptions<NoInfer_2<TData>, NoInfer_2<TVars>>, "id" | "query" | "optimistic" | "previousResult" | "returnPartialData">, Omit<Cache_2.ReadFragmentOptions<TData, TVars>, "id" | "variables" | "returnPartialData"> {
2477-
client?: ApolloClient<any>;
2478-
// (undocumented)
2474+
type UseSuspenseFragmentOptions<TData, TVariables extends OperationVariables> = {
2475+
fragment: DocumentNode | TypedDocumentNode<TData, TVariables>;
2476+
fragmentName?: string;
24792477
from: From<TData>;
2480-
// (undocumented)
24812478
optimistic?: boolean;
2482-
}
2479+
client?: ApolloClient<any>;
2480+
} & VariablesOption<NoInfer_2<TVariables>>;
24832481

24842482
// @public (undocumented)
24852483
export type UseSuspenseFragmentResult<TData> = {
@@ -2546,11 +2544,11 @@ export interface UseSuspenseQueryResult<TData = unknown, TVariables extends Oper
25462544
}
25472545

25482546
// @public (undocumented)
2549-
type VariablesOption<TVariables extends OperationVariables> = [
2547+
export type VariablesOption<TVariables extends OperationVariables> = [
25502548
TVariables
25512549
] extends [never] ? {
25522550
variables?: Record<string, never>;
2553-
} : {} extends OnlyRequiredProperties<TVariables> ? {
2551+
} : Record<string, never> extends OnlyRequiredProperties<TVariables> ? {
25542552
variables?: TVariables;
25552553
} : {
25562554
variables: TVariables;
@@ -2603,7 +2601,7 @@ interface WatchQueryOptions<TVariables extends OperationVariables = OperationVar
26032601
// src/react/hooks/useBackgroundQuery.ts:51:3 - (ae-forgotten-export) The symbol "FetchMoreFunction" needs to be exported by the entry point index.d.ts
26042602
// src/react/hooks/useBackgroundQuery.ts:75:4 - (ae-forgotten-export) The symbol "RefetchFunction" needs to be exported by the entry point index.d.ts
26052603
// src/react/hooks/useLoadableQuery.ts:120:9 - (ae-forgotten-export) The symbol "ResetFunction" needs to be exported by the entry point index.d.ts
2606-
// src/react/hooks/useSuspenseFragment.ts:60:5 - (ae-forgotten-export) The symbol "From" needs to be exported by the entry point index.d.ts
2604+
// src/react/hooks/useSuspenseFragment.ts:70:5 - (ae-forgotten-export) The symbol "From" needs to be exported by the entry point index.d.ts
26072605

26082606
// (No @packageDocumentation comment for this package)
26092607

.api-reports/api-report-react_hooks.api.md

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2305,14 +2305,16 @@ export function useSuspenseFragment<TData, TVariables extends OperationVariables
23052305
// @public (undocumented)
23062306
export function useSuspenseFragment<TData, TVariables extends OperationVariables = OperationVariables>(options: UseSuspenseFragmentOptions<TData, TVariables>): UseSuspenseFragmentResult<TData>;
23072307

2308+
// Warning: (ae-forgotten-export) The symbol "VariablesOption" needs to be exported by the entry point index.d.ts
2309+
//
23082310
// @public (undocumented)
2309-
interface UseSuspenseFragmentOptions<TData, TVars> extends Omit<Cache_2.DiffOptions<NoInfer_2<TData>, NoInfer_2<TVars>>, "id" | "query" | "optimistic" | "previousResult" | "returnPartialData">, Omit<Cache_2.ReadFragmentOptions<TData, TVars>, "id" | "variables" | "returnPartialData"> {
2310-
client?: ApolloClient<any>;
2311-
// (undocumented)
2311+
type UseSuspenseFragmentOptions<TData, TVariables extends OperationVariables> = {
2312+
fragment: DocumentNode | TypedDocumentNode<TData, TVariables>;
2313+
fragmentName?: string;
23122314
from: From<TData>;
2313-
// (undocumented)
23142315
optimistic?: boolean;
2315-
}
2316+
client?: ApolloClient<any>;
2317+
} & VariablesOption<NoInfer_2<TVariables>>;
23162318

23172319
// @public (undocumented)
23182320
export type UseSuspenseFragmentResult<TData> = {
@@ -2380,6 +2382,17 @@ export interface UseSuspenseQueryResult<TData = unknown, TVariables extends Oper
23802382
subscribeToMore: SubscribeToMoreFunction<TData, TVariables>;
23812383
}
23822384

2385+
// @public (undocumented)
2386+
type VariablesOption<TVariables extends OperationVariables> = [
2387+
TVariables
2388+
] extends [never] ? {
2389+
variables?: Record<string, never>;
2390+
} : Record<string, never> extends OnlyRequiredProperties<TVariables> ? {
2391+
variables?: TVariables;
2392+
} : {
2393+
variables: TVariables;
2394+
};
2395+
23832396
// @public
23842397
interface WatchFragmentOptions<TData, TVars> {
23852398
fragment: DocumentNode | TypedDocumentNode<TData, TVars>;
@@ -2427,7 +2440,7 @@ interface WatchQueryOptions<TVariables extends OperationVariables = OperationVar
24272440
// src/react/hooks/useBackgroundQuery.ts:51:3 - (ae-forgotten-export) The symbol "FetchMoreFunction" needs to be exported by the entry point index.d.ts
24282441
// src/react/hooks/useBackgroundQuery.ts:75:4 - (ae-forgotten-export) The symbol "RefetchFunction" needs to be exported by the entry point index.d.ts
24292442
// src/react/hooks/useLoadableQuery.ts:120:9 - (ae-forgotten-export) The symbol "ResetFunction" needs to be exported by the entry point index.d.ts
2430-
// src/react/hooks/useSuspenseFragment.ts:60:5 - (ae-forgotten-export) The symbol "From" needs to be exported by the entry point index.d.ts
2443+
// src/react/hooks/useSuspenseFragment.ts:70:5 - (ae-forgotten-export) The symbol "From" needs to be exported by the entry point index.d.ts
24312444

24322445
// (No @packageDocumentation comment for this package)
24332446

.api-reports/api-report-react_internal.api.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2368,13 +2368,13 @@ function useSuspenseFragment<TData, TVariables extends OperationVariables = Oper
23682368
function useSuspenseFragment<TData, TVariables extends OperationVariables = OperationVariables>(options: UseSuspenseFragmentOptions<TData, TVariables>): UseSuspenseFragmentResult<TData>;
23692369

23702370
// @public (undocumented)
2371-
interface UseSuspenseFragmentOptions<TData, TVars> extends Omit<Cache_2.DiffOptions<NoInfer_2<TData>, NoInfer_2<TVars>>, "id" | "query" | "optimistic" | "previousResult" | "returnPartialData">, Omit<Cache_2.ReadFragmentOptions<TData, TVars>, "id" | "variables" | "returnPartialData"> {
2372-
client?: ApolloClient<any>;
2373-
// (undocumented)
2371+
type UseSuspenseFragmentOptions<TData, TVariables extends OperationVariables> = {
2372+
fragment: DocumentNode | TypedDocumentNode<TData, TVariables>;
2373+
fragmentName?: string;
23742374
from: From<TData>;
2375-
// (undocumented)
23762375
optimistic?: boolean;
2377-
}
2376+
client?: ApolloClient<any>;
2377+
} & VariablesOption<NoInfer_2<TVariables>>;
23782378

23792379
// @public (undocumented)
23802380
type UseSuspenseFragmentResult<TData> = {
@@ -2448,7 +2448,7 @@ type VariablesOption<TVariables extends OperationVariables> = [
24482448
TVariables
24492449
] extends [never] ? {
24502450
variables?: Record<string, never>;
2451-
} : {} extends OnlyRequiredProperties<TVariables> ? {
2451+
} : Record<string, never> extends OnlyRequiredProperties<TVariables> ? {
24522452
variables?: TVariables;
24532453
} : {
24542454
variables: TVariables;
@@ -2549,9 +2549,9 @@ export function wrapQueryRef<TData, TVariables extends OperationVariables>(inter
25492549
// src/core/watchQueryOptions.ts:357:2 - (ae-forgotten-export) The symbol "UpdateQueryOptions" needs to be exported by the entry point index.d.ts
25502550
// src/react/hooks/useBackgroundQuery.ts:51:3 - (ae-forgotten-export) The symbol "FetchMoreFunction" needs to be exported by the entry point index.d.ts
25512551
// src/react/hooks/useBackgroundQuery.ts:75:4 - (ae-forgotten-export) The symbol "RefetchFunction" needs to be exported by the entry point index.d.ts
2552-
// src/react/hooks/useSuspenseFragment.ts:60:5 - (ae-forgotten-export) The symbol "From" needs to be exported by the entry point index.d.ts
2553-
// src/react/query-preloader/createQueryPreloader.ts:145:3 - (ae-forgotten-export) The symbol "PreloadQueryFetchPolicy" needs to be exported by the entry point index.d.ts
2554-
// src/react/query-preloader/createQueryPreloader.ts:167:5 - (ae-forgotten-export) The symbol "RefetchWritePolicy" needs to be exported by the entry point index.d.ts
2552+
// src/react/hooks/useSuspenseFragment.ts:70:5 - (ae-forgotten-export) The symbol "From" needs to be exported by the entry point index.d.ts
2553+
// src/react/query-preloader/createQueryPreloader.ts:77:5 - (ae-forgotten-export) The symbol "PreloadQueryFetchPolicy" needs to be exported by the entry point index.d.ts
2554+
// src/react/query-preloader/createQueryPreloader.ts:117:3 - (ae-forgotten-export) The symbol "RefetchWritePolicy" needs to be exported by the entry point index.d.ts
25552555

25562556
// (No @packageDocumentation comment for this package)
25572557

.api-reports/api-report.api.md

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2118,8 +2118,6 @@ export interface PreloadQueryFunction {
21182118
<TData = unknown, TVariables extends OperationVariables = OperationVariables>(query: DocumentNode | TypedDocumentNode<TData, TVariables>, ...[options]: PreloadQueryOptionsArg<NoInfer_2<TVariables>>): PreloadedQueryRef<TData, TVariables>;
21192119
}
21202120

2121-
// Warning: (ae-forgotten-export) The symbol "VariablesOption" needs to be exported by the entry point index.d.ts
2122-
//
21232121
// @public (undocumented)
21242122
export type PreloadQueryOptions<TVariables extends OperationVariables = OperationVariables> = {
21252123
canonizeResults?: boolean;
@@ -3134,13 +3132,13 @@ export function useSuspenseFragment<TData, TVariables extends OperationVariables
31343132
export function useSuspenseFragment<TData, TVariables extends OperationVariables = OperationVariables>(options: UseSuspenseFragmentOptions<TData, TVariables>): UseSuspenseFragmentResult<TData>;
31353133

31363134
// @public (undocumented)
3137-
interface UseSuspenseFragmentOptions<TData, TVars> extends Omit<Cache_2.DiffOptions<NoInfer_2<TData>, NoInfer_2<TVars>>, "id" | "query" | "optimistic" | "previousResult" | "returnPartialData">, Omit<Cache_2.ReadFragmentOptions<TData, TVars>, "id" | "variables" | "returnPartialData"> {
3138-
client?: ApolloClient<any>;
3139-
// (undocumented)
3135+
type UseSuspenseFragmentOptions<TData, TVariables extends OperationVariables> = {
3136+
fragment: DocumentNode | TypedDocumentNode<TData, TVariables>;
3137+
fragmentName?: string;
31403138
from: From<TData>;
3141-
// (undocumented)
31423139
optimistic?: boolean;
3143-
}
3140+
client?: ApolloClient<any>;
3141+
} & VariablesOption<NoInfer_2<TVariables>>;
31443142

31453143
// @public (undocumented)
31463144
export type UseSuspenseFragmentResult<TData> = {
@@ -3207,11 +3205,11 @@ export interface UseSuspenseQueryResult<TData = unknown, TVariables extends Oper
32073205
}
32083206

32093207
// @public (undocumented)
3210-
type VariablesOption<TVariables extends OperationVariables> = [
3208+
export type VariablesOption<TVariables extends OperationVariables> = [
32113209
TVariables
32123210
] extends [never] ? {
32133211
variables?: Record<string, never>;
3214-
} : {} extends OnlyRequiredProperties<TVariables> ? {
3212+
} : Record<string, never> extends OnlyRequiredProperties<TVariables> ? {
32153213
variables?: TVariables;
32163214
} : {
32173215
variables: TVariables;
@@ -3291,7 +3289,7 @@ interface WriteContext extends ReadMergeModifyContext {
32913289
// src/react/hooks/useBackgroundQuery.ts:51:3 - (ae-forgotten-export) The symbol "FetchMoreFunction" needs to be exported by the entry point index.d.ts
32923290
// src/react/hooks/useBackgroundQuery.ts:75:4 - (ae-forgotten-export) The symbol "RefetchFunction" needs to be exported by the entry point index.d.ts
32933291
// src/react/hooks/useLoadableQuery.ts:120:9 - (ae-forgotten-export) The symbol "ResetFunction" needs to be exported by the entry point index.d.ts
3294-
// src/react/hooks/useSuspenseFragment.ts:60:5 - (ae-forgotten-export) The symbol "From" needs to be exported by the entry point index.d.ts
3292+
// src/react/hooks/useSuspenseFragment.ts:70:5 - (ae-forgotten-export) The symbol "From" needs to be exported by the entry point index.d.ts
32953293

32963294
// (No @packageDocumentation comment for this package)
32973295

.size-limits.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
2-
"dist/apollo-client.min.cjs": 42235,
2+
"dist/apollo-client.min.cjs": 42240,
33
"import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 34448
44
}

src/react/hooks/__tests__/useSuspenseFragment.test.tsx

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
Masked,
1111
MaskedDocumentNode,
1212
MaybeMasked,
13+
OperationVariables,
1314
TypedDocumentNode,
1415
} from "../../../core";
1516
import React, { Suspense } from "react";
@@ -1847,4 +1848,160 @@ describe.skip("type tests", () => {
18471848
expectTypeOf(data).branded.toEqualTypeOf<Post | null>();
18481849
}
18491850
});
1851+
1852+
test("variables are optional and can be anything with an untyped DocumentNode", () => {
1853+
const fragment = gql``;
1854+
1855+
useSuspenseFragment({ fragment, from: null });
1856+
useSuspenseFragment({ fragment, from: null, variables: {} });
1857+
useSuspenseFragment({ fragment, from: null, variables: { foo: "bar" } });
1858+
useSuspenseFragment({ fragment, from: null, variables: { bar: "baz" } });
1859+
});
1860+
1861+
it("variables are optional and can be anything with unspecified TVariables on a TypedDocumentNode", () => {
1862+
const fragment: TypedDocumentNode<{ greeting: string }> = gql``;
1863+
1864+
useSuspenseFragment({ fragment, from: null });
1865+
useSuspenseFragment({ fragment, from: null, variables: {} });
1866+
useSuspenseFragment({ fragment, from: null, variables: { foo: "bar" } });
1867+
useSuspenseFragment({ fragment, from: null, variables: { bar: "baz" } });
1868+
});
1869+
1870+
it("variables are optional and can be anything with OperationVariables on a TypedDocumentNode", () => {
1871+
const fragment: TypedDocumentNode<
1872+
{ greeting: string },
1873+
OperationVariables
1874+
> = gql``;
1875+
1876+
useSuspenseFragment({ fragment, from: null });
1877+
useSuspenseFragment({ fragment, from: null, variables: {} });
1878+
useSuspenseFragment({ fragment, from: null, variables: { foo: "bar" } });
1879+
useSuspenseFragment({ fragment, from: null, variables: { bar: "baz" } });
1880+
});
1881+
1882+
it("variables are optional when TVariables are empty", () => {
1883+
const fragment: TypedDocumentNode<
1884+
{ greeting: string },
1885+
Record<string, never>
1886+
> = gql``;
1887+
1888+
useSuspenseFragment({ fragment, from: null });
1889+
useSuspenseFragment({ fragment, from: null, variables: {} });
1890+
// @ts-expect-error unknown variable
1891+
useSuspenseFragment({ fragment, from: null, variables: { foo: "bar" } });
1892+
});
1893+
1894+
it("does not allow variables when TVariables is `never`", () => {
1895+
const fragment: TypedDocumentNode<{ greeting: string }, never> = gql``;
1896+
1897+
useSuspenseFragment({ fragment, from: null });
1898+
useSuspenseFragment({ fragment, from: null, variables: {} });
1899+
// @ts-expect-error no variables argument allowed
1900+
useSuspenseFragment({ fragment, from: null, variables: { foo: "bar" } });
1901+
});
1902+
1903+
it("optional variables are optional to useSuspenseFragment", () => {
1904+
const fragment: TypedDocumentNode<{ posts: string[] }, { limit?: number }> =
1905+
gql``;
1906+
1907+
useSuspenseFragment({ fragment, from: null });
1908+
useSuspenseFragment({ fragment, from: null, variables: {} });
1909+
useSuspenseFragment({ fragment, from: null, variables: { limit: 10 } });
1910+
useSuspenseFragment({
1911+
fragment,
1912+
from: null,
1913+
variables: {
1914+
// @ts-expect-error unknown variable
1915+
foo: "bar",
1916+
},
1917+
});
1918+
useSuspenseFragment({
1919+
fragment,
1920+
from: null,
1921+
variables: {
1922+
limit: 10,
1923+
// @ts-expect-error unknown variable
1924+
foo: "bar",
1925+
},
1926+
});
1927+
});
1928+
1929+
it("enforces required variables when TVariables includes required variables", () => {
1930+
const fragment: TypedDocumentNode<{ character: string }, { id: string }> =
1931+
gql``;
1932+
1933+
// @ts-expect-error missing variables argument
1934+
useSuspenseFragment({ fragment, from: null });
1935+
// @ts-expect-error empty variables
1936+
useSuspenseFragment({ fragment, from: null, variables: {} });
1937+
useSuspenseFragment({ fragment, from: null, variables: { id: "1" } });
1938+
useSuspenseFragment({
1939+
fragment,
1940+
from: null,
1941+
variables: {
1942+
// @ts-expect-error unknown variable
1943+
foo: "bar",
1944+
},
1945+
});
1946+
useSuspenseFragment({
1947+
fragment,
1948+
from: null,
1949+
variables: {
1950+
id: "1",
1951+
// @ts-expect-error unknown variable
1952+
foo: "bar",
1953+
},
1954+
});
1955+
});
1956+
1957+
it("requires variables with mixed TVariables", () => {
1958+
const fragment: TypedDocumentNode<
1959+
{ character: string },
1960+
{ id: string; language?: string }
1961+
> = gql``;
1962+
1963+
// @ts-expect-error missing variables argument
1964+
useSuspenseFragment({ fragment, from: null });
1965+
// @ts-expect-error empty variables
1966+
useSuspenseFragment({ fragment, from: null, variables: {} });
1967+
useSuspenseFragment({ fragment, from: null, variables: { id: "1" } });
1968+
useSuspenseFragment({
1969+
fragment,
1970+
from: null,
1971+
// @ts-expect-error missing required variable
1972+
variables: { language: "en" },
1973+
});
1974+
useSuspenseFragment({
1975+
fragment,
1976+
from: null,
1977+
variables: { id: "1", language: "en" },
1978+
});
1979+
useSuspenseFragment({
1980+
fragment,
1981+
from: null,
1982+
variables: {
1983+
// @ts-expect-error unknown variable
1984+
foo: "bar",
1985+
},
1986+
});
1987+
useSuspenseFragment({
1988+
fragment,
1989+
from: null,
1990+
variables: {
1991+
id: "1",
1992+
// @ts-expect-error unknown variable
1993+
foo: "bar",
1994+
},
1995+
});
1996+
useSuspenseFragment({
1997+
fragment,
1998+
from: null,
1999+
variables: {
2000+
id: "1",
2001+
language: "en",
2002+
// @ts-expect-error unknown variable
2003+
foo: "bar",
2004+
},
2005+
});
2006+
});
18502007
});

0 commit comments

Comments
 (0)