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

+27-16
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

+43
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)