From db243c67cf24e04aa759cf6d9c641c5e023d943a Mon Sep 17 00:00:00 2001 From: "Gianmarco Rengucci (freshgiammi)" Date: Wed, 16 Oct 2024 20:15:48 +0200 Subject: [PATCH 1/2] feat(openapi-react-query): use queryOptions helper --- packages/openapi-react-query/src/index.ts | 43 ++++++++++++------- .../openapi-react-query/test/index.test.tsx | 43 +++++++++++++++++++ 2 files changed, 70 insertions(+), 16 deletions(-) diff --git a/packages/openapi-react-query/src/index.ts b/packages/openapi-react-query/src/index.ts index 855b249c9..be1a227a2 100644 --- a/packages/openapi-react-query/src/index.ts +++ b/packages/openapi-react-query/src/index.ts @@ -11,10 +11,13 @@ import { type QueryClient, type QueryFunctionContext, type SkipToken, + useInfiniteQuery, + type DataTag, useMutation, useQuery, - useSuspenseQuery, - useInfiniteQuery, + useSuspenseQuery, + dataTagSymbol, + dataTagErrorSymbol, } from "@tanstack/react-query"; import type { ClientMethod, @@ -34,8 +37,12 @@ export type QueryKey< Paths extends Record>, Method extends HttpMethod, Path extends PathsWithMethod, - Init = MaybeOptionalInit, -> = Init extends undefined ? readonly [Method, Path] : readonly [Method, Path, Init]; + Media extends MediaType, + Init extends MaybeOptionalInit = MaybeOptionalInit, +Response extends Required> = Required< + FetchResponse +>, +> = DataTag export type QueryOptionsFunction>, Media extends MediaType> = < Method extends HttpMethod, @@ -47,7 +54,7 @@ export type QueryOptionsFunction, - QueryKey + QueryKey >, "queryKey" | "queryFn" >, @@ -63,7 +70,7 @@ export type QueryOptionsFunction, - QueryKey + QueryKey >, "queryFn" > & { @@ -72,7 +79,7 @@ export type QueryOptionsFunction, - QueryKey + QueryKey >["queryFn"], SkipToken | undefined >; @@ -89,7 +96,7 @@ export type UseQueryMethod>, Response["data"], Response["error"], InferSelectReturnType, - QueryKey + QueryKey >, "queryKey" | "queryFn" >, @@ -112,7 +119,7 @@ export type UseInfiniteQueryMethod, Response["data"], - QueryKey, + QueryKey, unknown >, "queryKey" | "queryFn" @@ -137,7 +144,7 @@ export type UseSuspenseQueryMethod, - QueryKey + QueryKey >, "queryKey" | "queryFn" >, @@ -188,7 +195,7 @@ export default function createClient>({ queryKey: [method, path, init], signal, - }: QueryFunctionContext>) => { + }: QueryFunctionContext>) => { const mth = method.toUpperCase() as Uppercase; const fn = client[mth] as ClientMethod; 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 = (method, path, ...[init, options]) => ({ - queryKey: (init === undefined ? ([method, path] as const) : ([method, path, init] as const)) as QueryKey< - Paths, - typeof method, - typeof path - >, + queryKey: Object.assign(init === undefined ? [method, path] as const : [method, path, init] as const, { + [dataTagSymbol]: {} as any, + [dataTagErrorSymbol]: {} as any, + }) as QueryKey< + Paths, + typeof method, + typeof path, + Media + >, queryFn, ...options, }); diff --git a/packages/openapi-react-query/test/index.test.tsx b/packages/openapi-react-query/test/index.test.tsx index 65420164a..59c8cf183 100644 --- a/packages/openapi-react-query/test/index.test.tsx +++ b/packages/openapi-react-query/test/index.test.tsx @@ -91,6 +91,49 @@ describe("client", () => { client.queryOptions("get", "/blogposts/{post_id}", {}); }); + it("correctly infers return type from query key", async () => { + const fetchClient = createFetchClient({ baseUrl }); + const client = createClient(fetchClient); + + const initialData = { title: "Initial data", body: "Initial data" }; + + const options = client.queryOptions( + "get", + "/blogposts/{post_id}", + { + params: { + path: { + post_id: "1", + }, + }, + }, + { + initialData: () => initialData, + }, + ); + + const data = queryClient.getQueryData(options.queryKey); + + expectTypeOf(data).toEqualTypeOf< + | { + title: string; + body: string; + publish_date?: number; + } + | undefined + >(); + expect(data).toEqual(undefined); + + const { result } = renderHook(() => useQuery({ ...options, enabled: false }), { + wrapper, + }); + + await waitFor(() => expect(result.current.isFetching).toBe(false)); + + expect(result.current.data).toEqual(initialData); + expect(result.current.error).toBeNull(); + }); + it("returns query options that can resolve data correctly with fetchQuery", async () => { const response = { title: "title", body: "body" }; const fetchClient = createFetchClient({ baseUrl }); From fb53233e5fc4a71c26574ed3565592f4e08257e8 Mon Sep 17 00:00:00 2001 From: "Gianmarco Rengucci (freshgiammi)" Date: Mon, 27 Jan 2025 18:34:07 +0100 Subject: [PATCH 2/2] fix lint --- packages/openapi-react-query/src/index.ts | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/packages/openapi-react-query/src/index.ts b/packages/openapi-react-query/src/index.ts index be1a227a2..3569b3854 100644 --- a/packages/openapi-react-query/src/index.ts +++ b/packages/openapi-react-query/src/index.ts @@ -15,7 +15,7 @@ import { type DataTag, useMutation, useQuery, - useSuspenseQuery, + useSuspenseQuery, dataTagSymbol, dataTagErrorSymbol, } from "@tanstack/react-query"; @@ -39,10 +39,10 @@ export type QueryKey< Path extends PathsWithMethod, Media extends MediaType, Init extends MaybeOptionalInit = MaybeOptionalInit, -Response extends Required> = Required< - FetchResponse ->, -> = DataTag + Response extends Required> = Required< + FetchResponse + >, +> = DataTag; export type QueryOptionsFunction>, Media extends MediaType> = < Method extends HttpMethod, @@ -207,15 +207,10 @@ export default function createClient = (method, path, ...[init, options]) => ({ - queryKey: Object.assign(init === undefined ? [method, path] as const : [method, path, init] as const, { + queryKey: Object.assign(init === undefined ? ([method, path] as const) : ([method, path, init] as const), { [dataTagSymbol]: {} as any, [dataTagErrorSymbol]: {} as any, - }) as QueryKey< - Paths, - typeof method, - typeof path, - Media - >, + }) as QueryKey, queryFn, ...options, });