Skip to content

Commit 75ec8fb

Browse files
committed
last fixes
1 parent c145203 commit 75ec8fb

File tree

2 files changed

+75
-29
lines changed

2 files changed

+75
-29
lines changed

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

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,42 +11,68 @@ import {
1111
useQuery,
1212
useSuspenseQuery,
1313
queryOptions,
14-
type DataTag,
15-
type DefinedInitialDataOptions,
16-
type UndefinedInitialDataOptions,
1714
} from "@tanstack/react-query";
1815
import type { ClientMethod, FetchResponse, MaybeOptionalInit, Client as FetchClient } from "openapi-fetch";
1916
import type { HttpMethod, MediaType, PathsWithMethod, RequiredKeysOf } from "openapi-typescript-helpers";
2017

2118
type InitWithUnknowns<Init> = Init & { [key: string]: unknown };
2219

20+
// biome-ignore lint/suspicious/noRedeclare: <https://stackoverflow.com/questions/52760509/typescript-returntype-of-overloaded-function>
21+
type OverloadedReturnType<T> = T extends { (...args: any[]): infer R; (...args: any[]): infer R }
22+
? R
23+
: T extends (...args: any[]) => infer R
24+
? R
25+
: any;
26+
27+
type OverloadedParameters<T> = T extends { (...args: infer P1): any; (...args: infer P2): any }
28+
? P1
29+
: T extends (...args: infer P) => any
30+
? P
31+
: never;
32+
2333
export type QueryKey<
2434
Paths extends Record<string, Record<HttpMethod, {}>>,
2535
Method extends HttpMethod,
2636
Path extends PathsWithMethod<Paths, Method>,
2737
> = readonly [Method, Path, MaybeOptionalInit<Paths[Path], Method>];
2838

29-
export type QueryOptionsObject<Paths extends Record<string, Record<HttpMethod, {}>>, Media extends MediaType> = <
39+
export type QueryOptionsFunction<Paths extends Record<string, Record<HttpMethod, {}>>, Media extends MediaType> = <
3040
Method extends HttpMethod,
3141
Path extends PathsWithMethod<Paths, Method>,
3242
Init extends MaybeOptionalInit<Paths[Path], Method>,
3343
Response extends Required<FetchResponse<Paths[Path][Method], Init, Media>>, // note: Required is used to avoid repeating NonNullable in UseQuery types
3444
Options extends Omit<
3545
UseQueryOptions<Response["data"], Response["error"], Response["data"], QueryKey<Paths, Method, Path>>,
36-
"queryKey" | "queryFn" | "initialData"
46+
"queryKey" | "queryFn"
47+
>,
48+
>(
49+
method: Method,
50+
path: Path,
51+
...[init, options]: RequiredKeysOf<Init> extends never
52+
? [InitWithUnknowns<Init>?, Options?]
53+
: [InitWithUnknowns<Init>, Options?]
54+
) => UseQueryOptions<Response["data"], Response["error"], Response["data"], QueryKey<Paths, Method, Path>>;
55+
56+
export type QueryOptionsObject<Paths extends Record<string, Record<HttpMethod, {}>>, Media extends MediaType> = <
57+
Method extends HttpMethod,
58+
Path extends PathsWithMethod<Paths, Method>,
59+
Init extends MaybeOptionalInit<Paths[Path], Method>,
60+
Response extends Required<FetchResponse<Paths[Path][Method], Init, Media>>, // note: Required is used to avoid repeating NonNullable in UseQuery types
61+
Options extends Omit<
62+
OverloadedParameters<
63+
typeof queryOptions<Response["data"], Response["error"], Response["data"], QueryKey<Paths, Method, Path>>
64+
>[0],
65+
"queryKey" | "queryFn"
3766
>,
3867
>(
3968
method: Method,
4069
path: Path,
4170
...[init, options]: RequiredKeysOf<Init> extends never
4271
? [InitWithUnknowns<Init>?, Options?]
4372
: [InitWithUnknowns<Init>, Options?]
44-
) => (
45-
| DefinedInitialDataOptions<Response["data"], Response["error"], Response["data"], QueryKey<Paths, Method, Path>>
46-
| UndefinedInitialDataOptions<Response["data"], Response["error"], Response["data"], QueryKey<Paths, Method, Path>>
47-
) & {
48-
queryKey: DataTag<QueryKey<Paths, Method, Path>, Response["data"]>;
49-
};
73+
) => OverloadedReturnType<
74+
typeof queryOptions<Response["data"], Response["error"], Response["data"], QueryKey<Paths, Method, Path>>
75+
>;
5076

5177
export type UseQueryMethod<Paths extends Record<string, Record<HttpMethod, {}>>, Media extends MediaType> = <
5278
Method extends HttpMethod,
@@ -119,19 +145,24 @@ export default function createClient<Paths extends {}, Media extends MediaType =
119145
return data;
120146
};
121147

122-
const queryOptionsObject: QueryOptionsObject<Paths, Media> = (method, path, ...[init, options]) =>
123-
queryOptions({
124-
queryKey: [method, path, init as InitWithUnknowns<typeof init>] as const,
125-
queryFn,
126-
...options,
127-
});
148+
const queryOptionsParams: QueryOptionsFunction<Paths, Media> = (method, path, ...[init, options]) => ({
149+
queryKey: [method, path, init as InitWithUnknowns<typeof init>] as const,
150+
queryFn,
151+
...options,
152+
});
128153

129154
return {
130-
queryOptions: queryOptionsObject,
155+
queryOptions: (method, path, ...[init, options]) =>
156+
queryOptions({
157+
queryKey: [method, path, init as InitWithUnknowns<typeof init>] as const,
158+
queryFn,
159+
// TODO: find a way to avoid as any
160+
...(options as any),
161+
}),
131162
useQuery: (method, path, ...[init, options, queryClient]) =>
132-
useQuery(queryOptionsObject(method, path, init as InitWithUnknowns<typeof init>, options), queryClient),
163+
useQuery(queryOptionsParams(method, path, init as InitWithUnknowns<typeof init>, options), queryClient),
133164
useSuspenseQuery: (method, path, ...[init, options, queryClient]) =>
134-
useSuspenseQuery(queryOptionsObject(method, path, init as InitWithUnknowns<typeof init>, options), queryClient),
165+
useSuspenseQuery(queryOptionsParams(method, path, init as InitWithUnknowns<typeof init>, options), queryClient),
135166
useMutation: (method, path, options, queryClient) =>
136167
useMutation(
137168
{

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

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { paths } from "./fixtures/api.js";
44
import createClient from "../src/index.js";
55
import createFetchClient from "openapi-fetch";
66
import { fireEvent, render, renderHook, screen, waitFor, act } from "@testing-library/react";
7-
import { QueryClient, QueryClientProvider, useQueries } from "@tanstack/react-query";
7+
import { QueryClient, QueryClientProvider, useQueries, useQuery } from "@tanstack/react-query";
88
import { Suspense, type ReactNode } from "react";
99
import { ErrorBoundary } from "react-error-boundary";
1010

@@ -68,21 +68,36 @@ describe("client", () => {
6868
const fetchClient = createFetchClient<paths>({ baseUrl });
6969
const client = createClient(fetchClient);
7070

71-
const data = queryClient.getQueryData(
72-
client.queryOptions("get", "/blogposts/{post_id}", {
73-
params: {
74-
path: {
75-
post_id: "1",
76-
},
71+
const initialData = { title: "Initial data", body: "Initial data" };
72+
73+
const options = client.queryOptions("get", "/blogposts/{post_id}", {
74+
params: {
75+
path: {
76+
post_id: "1",
7777
},
78-
}).queryKey
79-
);
78+
},
79+
},{
80+
initialData: () => initialData
81+
})
8082

83+
const data = queryClient.getQueryData( options.queryKey );
84+
8185
expectTypeOf(data).toEqualTypeOf<{
8286
title: string;
8387
body: string;
8488
publish_date?: number;
8589
} | undefined>();
90+
expect(data).toEqual(undefined);
91+
92+
const { result } = renderHook(() => useQuery({...options, enabled: false}), {
93+
wrapper,
94+
});
95+
96+
await waitFor(() => expect(result.current.isFetching).toBe(false));
97+
98+
expect(result.current.data).toEqual(initialData);
99+
expect(result.current.error).toBeNull();
100+
86101

87102

88103
});

0 commit comments

Comments
 (0)