Skip to content

Commit db243c6

Browse files
committed
feat(openapi-react-query): use queryOptions helper
1 parent 31bdbaf commit db243c6

File tree

2 files changed

+70
-16
lines changed

2 files changed

+70
-16
lines changed

packages/openapi-react-query/src/index.ts

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@ import {
1111
type QueryClient,
1212
type QueryFunctionContext,
1313
type SkipToken,
14+
useInfiniteQuery,
15+
type DataTag,
1416
useMutation,
1517
useQuery,
16-
useSuspenseQuery,
17-
useInfiniteQuery,
18+
useSuspenseQuery,
19+
dataTagSymbol,
20+
dataTagErrorSymbol,
1821
} from "@tanstack/react-query";
1922
import type {
2023
ClientMethod,
@@ -34,8 +37,12 @@ export type QueryKey<
3437
Paths extends Record<string, Record<HttpMethod, {}>>,
3538
Method extends HttpMethod,
3639
Path extends PathsWithMethod<Paths, Method>,
37-
Init = MaybeOptionalInit<Paths[Path], Method>,
38-
> = Init extends undefined ? readonly [Method, Path] : readonly [Method, Path, Init];
40+
Media extends MediaType,
41+
Init extends MaybeOptionalInit<Paths[Path], Method> = MaybeOptionalInit<Paths[Path], Method>,
42+
Response extends Required<FetchResponse<Paths[Path][Method], Init, Media>> = Required<
43+
FetchResponse<Paths[Path][Method], Init, Media>
44+
>,
45+
> = DataTag<readonly [Method, Path, Init], Response["data"]>
3946

4047
export type QueryOptionsFunction<Paths extends Record<string, Record<HttpMethod, {}>>, Media extends MediaType> = <
4148
Method extends HttpMethod,
@@ -47,7 +54,7 @@ export type QueryOptionsFunction<Paths extends Record<string, Record<HttpMethod,
4754
Response["data"],
4855
Response["error"],
4956
InferSelectReturnType<Response["data"], Options["select"]>,
50-
QueryKey<Paths, Method, Path>
57+
QueryKey<Paths, Method, Path, Media>
5158
>,
5259
"queryKey" | "queryFn"
5360
>,
@@ -63,7 +70,7 @@ export type QueryOptionsFunction<Paths extends Record<string, Record<HttpMethod,
6370
Response["data"],
6471
Response["error"],
6572
InferSelectReturnType<Response["data"], Options["select"]>,
66-
QueryKey<Paths, Method, Path>
73+
QueryKey<Paths, Method, Path, Media>
6774
>,
6875
"queryFn"
6976
> & {
@@ -72,7 +79,7 @@ export type QueryOptionsFunction<Paths extends Record<string, Record<HttpMethod,
7279
Response["data"],
7380
Response["error"],
7481
InferSelectReturnType<Response["data"], Options["select"]>,
75-
QueryKey<Paths, Method, Path>
82+
QueryKey<Paths, Method, Path, Media>
7683
>["queryFn"],
7784
SkipToken | undefined
7885
>;
@@ -89,7 +96,7 @@ export type UseQueryMethod<Paths extends Record<string, Record<HttpMethod, {}>>,
8996
Response["data"],
9097
Response["error"],
9198
InferSelectReturnType<Response["data"], Options["select"]>,
92-
QueryKey<Paths, Method, Path>
99+
QueryKey<Paths, Method, Path, Media>
93100
>,
94101
"queryKey" | "queryFn"
95102
>,
@@ -112,7 +119,7 @@ export type UseInfiniteQueryMethod<Paths extends Record<string, Record<HttpMetho
112119
Response["error"],
113120
InfiniteData<Response["data"]>,
114121
Response["data"],
115-
QueryKey<Paths, Method, Path>,
122+
QueryKey<Paths, Method, Path, Media>,
116123
unknown
117124
>,
118125
"queryKey" | "queryFn"
@@ -137,7 +144,7 @@ export type UseSuspenseQueryMethod<Paths extends Record<string, Record<HttpMetho
137144
Response["data"],
138145
Response["error"],
139146
InferSelectReturnType<Response["data"], Options["select"]>,
140-
QueryKey<Paths, Method, Path>
147+
QueryKey<Paths, Method, Path, Media>
141148
>,
142149
"queryKey" | "queryFn"
143150
>,
@@ -188,7 +195,7 @@ export default function createClient<Paths extends {}, Media extends MediaType =
188195
const queryFn = async <Method extends HttpMethod, Path extends PathsWithMethod<Paths, Method>>({
189196
queryKey: [method, path, init],
190197
signal,
191-
}: QueryFunctionContext<QueryKey<Paths, Method, Path>>) => {
198+
}: QueryFunctionContext<QueryKey<Paths, Method, Path, Media>>) => {
192199
const mth = method.toUpperCase() as Uppercase<typeof method>;
193200
const fn = client[mth] as ClientMethod<Paths, typeof method, Media>;
194201
const { data, error } = await fn(path, { signal, ...(init as any) }); // TODO: find a way to avoid as any
@@ -200,11 +207,15 @@ export default function createClient<Paths extends {}, Media extends MediaType =
200207
};
201208

202209
const queryOptions: QueryOptionsFunction<Paths, Media> = (method, path, ...[init, options]) => ({
203-
queryKey: (init === undefined ? ([method, path] as const) : ([method, path, init] as const)) as QueryKey<
204-
Paths,
205-
typeof method,
206-
typeof path
207-
>,
210+
queryKey: Object.assign(init === undefined ? [method, path] as const : [method, path, init] as const, {
211+
[dataTagSymbol]: {} as any,
212+
[dataTagErrorSymbol]: {} as any,
213+
}) as QueryKey<
214+
Paths,
215+
typeof method,
216+
typeof path,
217+
Media
218+
>,
208219
queryFn,
209220
...options,
210221
});

packages/openapi-react-query/test/index.test.tsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,49 @@ describe("client", () => {
9191
client.queryOptions("get", "/blogposts/{post_id}", {});
9292
});
9393

94+
it("correctly infers return type from query key", async () => {
95+
const fetchClient = createFetchClient<paths>({ baseUrl });
96+
const client = createClient(fetchClient);
97+
98+
const initialData = { title: "Initial data", body: "Initial data" };
99+
100+
const options = client.queryOptions(
101+
"get",
102+
"/blogposts/{post_id}",
103+
{
104+
params: {
105+
path: {
106+
post_id: "1",
107+
},
108+
},
109+
},
110+
{
111+
initialData: () => initialData,
112+
},
113+
);
114+
115+
const data = queryClient.getQueryData(options.queryKey);
116+
117+
expectTypeOf(data).toEqualTypeOf<
118+
| {
119+
title: string;
120+
body: string;
121+
publish_date?: number;
122+
}
123+
| undefined
124+
>();
125+
expect(data).toEqual(undefined);
126+
127+
const { result } = renderHook(() => useQuery({ ...options, enabled: false }), {
128+
wrapper,
129+
});
130+
131+
await waitFor(() => expect(result.current.isFetching).toBe(false));
132+
133+
expect(result.current.data).toEqual(initialData);
134+
expect(result.current.error).toBeNull();
135+
});
136+
94137
it("returns query options that can resolve data correctly with fetchQuery", async () => {
95138
const response = { title: "title", body: "body" };
96139
const fetchClient = createFetchClient<paths>({ baseUrl });

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy