From fc4e1d15bfb7ac7ff6490d867f34bed3c4defd3f Mon Sep 17 00:00:00 2001 From: Adam Michel <adam@amichel.me> Date: Mon, 24 Feb 2025 15:00:12 -0800 Subject: [PATCH 1/5] Infer select return from useInfiniteQuery --- packages/openapi-react-query/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/openapi-react-query/src/index.ts b/packages/openapi-react-query/src/index.ts index 855b249c9..88a1753f9 100644 --- a/packages/openapi-react-query/src/index.ts +++ b/packages/openapi-react-query/src/index.ts @@ -110,7 +110,7 @@ export type UseInfiniteQueryMethod<Paths extends Record<string, Record<HttpMetho UseInfiniteQueryOptions< Response["data"], Response["error"], - InfiniteData<Response["data"]>, + InferSelectReturnType<InfiniteData<Response["data"]>, Options["select"]>, Response["data"], QueryKey<Paths, Method, Path>, unknown @@ -125,7 +125,7 @@ export type UseInfiniteQueryMethod<Paths extends Record<string, Record<HttpMetho init: InitWithUnknowns<Init>, options: Options, queryClient?: QueryClient, -) => UseInfiniteQueryResult<InfiniteData<Response["data"]>, Response["error"]>; +) => UseInfiniteQueryResult<InferSelectReturnType<InfiniteData<Response["data"]>, Options["select"]>, Response["error"]>; export type UseSuspenseQueryMethod<Paths extends Record<string, Record<HttpMethod, {}>>, Media extends MediaType> = < Method extends HttpMethod, From d7853fa275a72056a60cb8c50475129ef1cf7410 Mon Sep 17 00:00:00 2001 From: Adam Michel <adam@amichel.me> Date: Mon, 24 Feb 2025 15:08:07 -0800 Subject: [PATCH 2/5] Include useInfiniteQuery in docs and update --- docs/.vitepress/en.ts | 4 ++++ docs/openapi-react-query/use-infinite-query.md | 1 + 2 files changed, 5 insertions(+) diff --git a/docs/.vitepress/en.ts b/docs/.vitepress/en.ts index a15fe27cb..4fa344bdd 100644 --- a/docs/.vitepress/en.ts +++ b/docs/.vitepress/en.ts @@ -80,6 +80,10 @@ export default defineConfig({ text: "useSuspenseQuery", link: "/use-suspense-query", }, + { + text: "useInfiniteQuery", + link: "/use-infinite-query", + }, { text: "queryOptions", link: "/query-options", diff --git a/docs/openapi-react-query/use-infinite-query.md b/docs/openapi-react-query/use-infinite-query.md index 86bcbad1e..7824baa1a 100644 --- a/docs/openapi-react-query/use-infinite-query.md +++ b/docs/openapi-react-query/use-infinite-query.md @@ -105,6 +105,7 @@ const query = $api.useInfiniteQuery( - Only required if the OpenApi schema requires parameters. - The options `params` are used as key. See [Query Keys](https://tanstack.com/query/latest/docs/framework/react/guides/query-keys) for more information. - `infiniteQueryOptions` + - `pageParamName` The query param name used for pagination, `"cursor"` by default. - The original `useInfiniteQuery` options. - [See more information](https://tanstack.com/query/latest/docs/framework/react/reference/useInfiniteQuery) - `queryClient` From c457082aef0600db50f875f1c959415a52a48436 Mon Sep 17 00:00:00 2001 From: Adam Michel <adam@amichel.me> Date: Mon, 24 Feb 2025 15:21:25 -0800 Subject: [PATCH 3/5] Add type test for selects on useInfiniteQuery --- .../openapi-react-query/test/index.test.tsx | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/packages/openapi-react-query/test/index.test.tsx b/packages/openapi-react-query/test/index.test.tsx index 5ccda7a42..3dd84f87a 100644 --- a/packages/openapi-react-query/test/index.test.tsx +++ b/packages/openapi-react-query/test/index.test.tsx @@ -1087,5 +1087,69 @@ describe("client", () => { const allItems = result.current.data?.pages.flatMap((page) => page.items); expect(allItems).toEqual([1, 2, 3, 4, 5, 6]); }); + it('should use return type from select option', async () => { + const fetchClient = createFetchClient<paths>({ baseUrl }); + const client = createClient(fetchClient); + + // First page request handler + const firstRequestHandler = useMockRequestHandler({ + baseUrl, + method: "get", + path: "/paginated-data", + status: 200, + body: { items: [1, 2, 3], nextPage: 1 }, + }); + + const { result, rerender } = renderHook( + () => + client.useInfiniteQuery( + "get", + "/paginated-data", + { + params: { + query: { + limit: 3, + }, + }, + }, + { + getNextPageParam: (lastPage) => lastPage.nextPage, + initialPageParam: 0, + select: (data) => data.pages.flatMap((page) => page.items).filter((item) => item !== undefined), + }, + ), + { wrapper }, + ); + + // Wait for initial query to complete + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + + expectTypeOf(result.current.data).toEqualTypeOf<number[] | undefined>(); + expect(result.current.data).toEqual([1, 2, 3]); + + // Set up mock for second page before triggering next page fetch + const secondRequestHandler = useMockRequestHandler({ + baseUrl, + method: "get", + path: "/paginated-data", + status: 200, + body: { items: [4, 5, 6], nextPage: 2 }, + }); + + // Fetch next page + await act(async () => { + await result.current.fetchNextPage(); + // Force a rerender to ensure state is updated + rerender(); + }); + + // Wait for second page to be fetched and verify loading states + await waitFor(() => { + expect(result.current.isFetching).toBe(false); + expect(result.current.hasNextPage).toBe(true); + }); + + expect(result.current.data).toEqual([1, 2, 3, 4, 5, 6]); + }); }); }); From 16ba2147446c81ece56467f7a7563c757faa2515 Mon Sep 17 00:00:00 2001 From: Adam Michel <adam@amichel.me> Date: Mon, 24 Feb 2025 15:30:05 -0800 Subject: [PATCH 4/5] Add changeset --- .changeset/seven-monkeys-fetch.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/seven-monkeys-fetch.md diff --git a/.changeset/seven-monkeys-fetch.md b/.changeset/seven-monkeys-fetch.md new file mode 100644 index 000000000..10e4e6e32 --- /dev/null +++ b/.changeset/seven-monkeys-fetch.md @@ -0,0 +1,5 @@ +--- +"openapi-react-query": minor +--- + +[#2169](https://github.com/openapi-ts/openapi-typescript/pull/2169): Infer returned `data` type from `select` option when used with the `useInfiniteQuery` method. From 78225d21435cf44e8f80532c45eb30163dd464c8 Mon Sep 17 00:00:00 2001 From: Adam Michel <adam@amichel.me> Date: Mon, 24 Feb 2025 15:34:21 -0800 Subject: [PATCH 5/5] Fix linter errors --- packages/openapi-react-query/src/index.ts | 5 ++++- packages/openapi-react-query/test/index.test.tsx | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/openapi-react-query/src/index.ts b/packages/openapi-react-query/src/index.ts index 88a1753f9..97fd9ce1e 100644 --- a/packages/openapi-react-query/src/index.ts +++ b/packages/openapi-react-query/src/index.ts @@ -125,7 +125,10 @@ export type UseInfiniteQueryMethod<Paths extends Record<string, Record<HttpMetho init: InitWithUnknowns<Init>, options: Options, queryClient?: QueryClient, -) => UseInfiniteQueryResult<InferSelectReturnType<InfiniteData<Response["data"]>, Options["select"]>, Response["error"]>; +) => UseInfiniteQueryResult< + InferSelectReturnType<InfiniteData<Response["data"]>, Options["select"]>, + Response["error"] +>; export type UseSuspenseQueryMethod<Paths extends Record<string, Record<HttpMethod, {}>>, Media extends MediaType> = < Method extends HttpMethod, diff --git a/packages/openapi-react-query/test/index.test.tsx b/packages/openapi-react-query/test/index.test.tsx index 3dd84f87a..1062bafd7 100644 --- a/packages/openapi-react-query/test/index.test.tsx +++ b/packages/openapi-react-query/test/index.test.tsx @@ -1087,7 +1087,7 @@ describe("client", () => { const allItems = result.current.data?.pages.flatMap((page) => page.items); expect(allItems).toEqual([1, 2, 3, 4, 5, 6]); }); - it('should use return type from select option', async () => { + it("should use return type from select option", async () => { const fetchClient = createFetchClient<paths>({ baseUrl }); const client = createClient(fetchClient);