diff --git a/examples/react-app/src/components/SuspenseChild.tsx b/examples/react-app/src/components/SuspenseChild.tsx index 040be32..c1399c1 100644 --- a/examples/react-app/src/components/SuspenseChild.tsx +++ b/examples/react-app/src/components/SuspenseChild.tsx @@ -1,17 +1,14 @@ import { useFindPetsSuspense } from "../../openapi/queries/suspense"; export const SuspenseChild = () => { - const { data, error } = useFindPetsSuspense({ + // useSuspenseQuery enforces throwOnError: true, so errors are thrown and caught by ErrorBoundary + const { data } = useFindPetsSuspense({ query: { tags: [], limit: 10 }, }); - console.log({ error }); - if (!Array.isArray(data)) { - return
Error!
; - } return ( diff --git a/src/createImports.mts b/src/createImports.mts index ac887ec..9c0f1ce 100644 --- a/src/createImports.mts +++ b/src/createImports.mts @@ -102,6 +102,11 @@ export const createImports = ({ undefined, ts.factory.createIdentifier("UseMutationResult"), ), + ts.factory.createImportSpecifier( + false, + undefined, + ts.factory.createIdentifier("UseSuspenseQueryOptions"), + ), ]), ), ts.factory.createStringLiteral("@tanstack/react-query"), diff --git a/src/createUseQuery.mts b/src/createUseQuery.mts index f626641..53b43d8 100644 --- a/src/createUseQuery.mts +++ b/src/createUseQuery.mts @@ -60,6 +60,17 @@ const createApiResponseType = ({ ts.factory.createTypeReferenceNode(BuildCommonTypeName(apiResponse.name)), ); + // Response data type for suspense - wrap with NonNullable to exclude undefined + const suspenseResponseDataType = ts.factory.createTypeParameterDeclaration( + undefined, + TData.text, + undefined, + ts.factory.createTypeReferenceNode( + ts.factory.createIdentifier("NonNullable"), + [ts.factory.createTypeReferenceNode(BuildCommonTypeName(apiResponse.name))], + ), + ); + const responseErrorType = ts.factory.createTypeParameterDeclaration( undefined, TError.text, @@ -93,6 +104,12 @@ const createApiResponseType = ({ * MyClassMethodDefaultResponse */ responseDataType, + /** + * ResponseDataType for suspense - wrap with NonNullable to exclude undefined + * + * NonNullable + */ + suspenseResponseDataType, /** * ErrorDataType * @@ -202,6 +219,7 @@ function createQueryHook({ } const isInfiniteQuery = queryString === "useInfiniteQuery"; + const isSuspenseQuery = queryString === "useSuspenseQuery"; const responseDataTypeRef = responseDataType.default as ts.TypeReferenceNode; const responseDataTypeIdentifier = @@ -266,7 +284,9 @@ function createQueryHook({ ts.factory.createIdentifier( isInfiniteQuery ? "UseInfiniteQueryOptions" - : "UseQueryOptions", + : isSuspenseQuery + ? "UseSuspenseQueryOptions" + : "UseQueryOptions", ), [ ts.factory.createTypeReferenceNode(TData), @@ -469,6 +489,7 @@ export const createUseQuery = ({ const { apiResponse: defaultApiResponse, responseDataType, + suspenseResponseDataType, responseErrorType, } = createApiResponseType({ methodName, @@ -496,7 +517,7 @@ export const createUseQuery = ({ const suspenseQueryHook = createQueryHook({ queryString: "useSuspenseQuery", suffix: "Suspense", - responseDataType, + responseDataType: suspenseResponseDataType, responseErrorType, requestParams, method, diff --git a/tests/__snapshots__/createSource.test.ts.snap b/tests/__snapshots__/createSource.test.ts.snap index eb73615..5face87 100644 --- a/tests/__snapshots__/createSource.test.ts.snap +++ b/tests/__snapshots__/createSource.test.ts.snap @@ -12,7 +12,7 @@ exports[`createSource > createSource - @hey-api/client-axios 2`] = ` "// generated with @7nohe/openapi-react-query-codegen@1.0.0 import { type Options } from "@hey-api/client-axios"; -import { type QueryClient, useQuery, useSuspenseQuery, useMutation, UseQueryResult, UseQueryOptions, UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; +import { type QueryClient, useQuery, useSuspenseQuery, useMutation, UseQueryResult, UseQueryOptions, UseMutationOptions, UseMutationResult, UseSuspenseQueryOptions } from "@tanstack/react-query"; import { client, findPets, addPet, getNotDefined, postNotDefined, findPetById, deletePet, findPaginatedPets } from "../requests/services.gen"; import { Pet, NewPet, Error, FindPetsData, FindPetsResponse, FindPetsError, AddPetData, AddPetResponse, AddPetError, GetNotDefinedResponse, GetNotDefinedError, PostNotDefinedResponse, PostNotDefinedError, FindPetByIdData, FindPetByIdResponse, FindPetByIdError, DeletePetData, DeletePetResponse, DeletePetError, FindPaginatedPetsData, FindPaginatedPetsResponse, FindPaginatedPetsError } from "../requests/types.gen"; import { AxiosError } from "axios"; @@ -49,7 +49,7 @@ exports[`createSource > createSource - @hey-api/client-axios 3`] = ` import * as Common from "./common"; import { type Options } from "@hey-api/client-axios"; -import { type QueryClient, useQuery, useSuspenseQuery, useMutation, UseQueryResult, UseQueryOptions, UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; +import { type QueryClient, useQuery, useSuspenseQuery, useMutation, UseQueryResult, UseQueryOptions, UseMutationOptions, UseMutationResult, UseSuspenseQueryOptions } from "@tanstack/react-query"; import { client, findPets, addPet, getNotDefined, postNotDefined, findPetById, deletePet, findPaginatedPets } from "../requests/services.gen"; import { Pet, NewPet, Error, FindPetsData, FindPetsResponse, FindPetsError, AddPetData, AddPetResponse, AddPetError, GetNotDefinedResponse, GetNotDefinedError, PostNotDefinedResponse, PostNotDefinedError, FindPetByIdData, FindPetByIdResponse, FindPetByIdError, DeletePetData, DeletePetResponse, DeletePetError, FindPaginatedPetsData, FindPaginatedPetsResponse, FindPaginatedPetsError } from "../requests/types.gen"; import { AxiosError } from "axios"; @@ -68,14 +68,14 @@ exports[`createSource > createSource - @hey-api/client-axios 4`] = ` import * as Common from "./common"; import { type Options } from "@hey-api/client-axios"; -import { type QueryClient, useQuery, useSuspenseQuery, useMutation, UseQueryResult, UseQueryOptions, UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; +import { type QueryClient, useQuery, useSuspenseQuery, useMutation, UseQueryResult, UseQueryOptions, UseMutationOptions, UseMutationResult, UseSuspenseQueryOptions } from "@tanstack/react-query"; import { client, findPets, addPet, getNotDefined, postNotDefined, findPetById, deletePet, findPaginatedPets } from "../requests/services.gen"; import { Pet, NewPet, Error, FindPetsData, FindPetsResponse, FindPetsError, AddPetData, AddPetResponse, AddPetError, GetNotDefinedResponse, GetNotDefinedError, PostNotDefinedResponse, PostNotDefinedError, FindPetByIdData, FindPetByIdResponse, FindPetByIdError, DeletePetData, DeletePetResponse, DeletePetError, FindPaginatedPetsData, FindPaginatedPetsResponse, FindPaginatedPetsError } from "../requests/types.gen"; import { AxiosError } from "axios"; -export const useFindPetsSuspense = , TQueryKey extends Array = unknown[]>(clientOptions: Options = {}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseFindPetsKeyFn(clientOptions, queryKey), queryFn: () => findPets({ ...clientOptions }).then(response => response.data as TData) as TData, ...options }); -export const useGetNotDefinedSuspense = , TQueryKey extends Array = unknown[]>(clientOptions: Options = {}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseGetNotDefinedKeyFn(clientOptions, queryKey), queryFn: () => getNotDefined({ ...clientOptions }).then(response => response.data as TData) as TData, ...options }); -export const useFindPetByIdSuspense = , TQueryKey extends Array = unknown[]>(clientOptions: Options, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseFindPetByIdKeyFn(clientOptions, queryKey), queryFn: () => findPetById({ ...clientOptions }).then(response => response.data as TData) as TData, ...options }); -export const useFindPaginatedPetsSuspense = , TQueryKey extends Array = unknown[]>(clientOptions: Options = {}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseFindPaginatedPetsKeyFn(clientOptions, queryKey), queryFn: () => findPaginatedPets({ ...clientOptions }).then(response => response.data as TData) as TData, ...options }); +export const useFindPetsSuspense = , TError = AxiosError, TQueryKey extends Array = unknown[]>(clientOptions: Options = {}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseFindPetsKeyFn(clientOptions, queryKey), queryFn: () => findPets({ ...clientOptions }).then(response => response.data as TData) as TData, ...options }); +export const useGetNotDefinedSuspense = , TError = AxiosError, TQueryKey extends Array = unknown[]>(clientOptions: Options = {}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseGetNotDefinedKeyFn(clientOptions, queryKey), queryFn: () => getNotDefined({ ...clientOptions }).then(response => response.data as TData) as TData, ...options }); +export const useFindPetByIdSuspense = , TError = AxiosError, TQueryKey extends Array = unknown[]>(clientOptions: Options, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseFindPetByIdKeyFn(clientOptions, queryKey), queryFn: () => findPetById({ ...clientOptions }).then(response => response.data as TData) as TData, ...options }); +export const useFindPaginatedPetsSuspense = , TError = AxiosError, TQueryKey extends Array = unknown[]>(clientOptions: Options = {}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseFindPaginatedPetsKeyFn(clientOptions, queryKey), queryFn: () => findPaginatedPets({ ...clientOptions }).then(response => response.data as TData) as TData, ...options }); " `; @@ -84,7 +84,7 @@ exports[`createSource > createSource - @hey-api/client-axios 5`] = ` import * as Common from "./common"; import { type Options } from "@hey-api/client-axios"; -import { type QueryClient, useQuery, useSuspenseQuery, useMutation, UseQueryResult, UseQueryOptions, UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; +import { type QueryClient, useQuery, useSuspenseQuery, useMutation, UseQueryResult, UseQueryOptions, UseMutationOptions, UseMutationResult, UseSuspenseQueryOptions } from "@tanstack/react-query"; import { client, findPets, addPet, getNotDefined, postNotDefined, findPetById, deletePet, findPaginatedPets } from "../requests/services.gen"; import { Pet, NewPet, Error, FindPetsData, FindPetsResponse, FindPetsError, AddPetData, AddPetResponse, AddPetError, GetNotDefinedResponse, GetNotDefinedError, PostNotDefinedResponse, PostNotDefinedError, FindPetByIdData, FindPetByIdResponse, FindPetByIdError, DeletePetData, DeletePetResponse, DeletePetError, FindPaginatedPetsData, FindPaginatedPetsResponse, FindPaginatedPetsError } from "../requests/types.gen"; import { AxiosError } from "axios"; @@ -107,7 +107,7 @@ exports[`createSource > createSource - @hey-api/client-fetch 2`] = ` "// generated with @7nohe/openapi-react-query-codegen@1.0.0 import { type Options } from "@hey-api/client-fetch"; -import { type QueryClient, useQuery, useSuspenseQuery, useMutation, UseQueryResult, UseQueryOptions, UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; +import { type QueryClient, useQuery, useSuspenseQuery, useMutation, UseQueryResult, UseQueryOptions, UseMutationOptions, UseMutationResult, UseSuspenseQueryOptions } from "@tanstack/react-query"; import { client, findPets, addPet, getNotDefined, postNotDefined, findPetById, deletePet, findPaginatedPets } from "../requests/services.gen"; import { Pet, NewPet, Error, FindPetsData, FindPetsResponse, FindPetsError, AddPetData, AddPetResponse, AddPetError, GetNotDefinedResponse, GetNotDefinedError, PostNotDefinedResponse, PostNotDefinedError, FindPetByIdData, FindPetByIdResponse, FindPetByIdError, DeletePetData, DeletePetResponse, DeletePetError, FindPaginatedPetsData, FindPaginatedPetsResponse, FindPaginatedPetsError } from "../requests/types.gen"; export type FindPetsDefaultResponse = Awaited>["data"]; @@ -143,7 +143,7 @@ exports[`createSource > createSource - @hey-api/client-fetch 3`] = ` import * as Common from "./common"; import { type Options } from "@hey-api/client-fetch"; -import { type QueryClient, useQuery, useSuspenseQuery, useMutation, UseQueryResult, UseQueryOptions, UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; +import { type QueryClient, useQuery, useSuspenseQuery, useMutation, UseQueryResult, UseQueryOptions, UseMutationOptions, UseMutationResult, UseSuspenseQueryOptions } from "@tanstack/react-query"; import { client, findPets, addPet, getNotDefined, postNotDefined, findPetById, deletePet, findPaginatedPets } from "../requests/services.gen"; import { Pet, NewPet, Error, FindPetsData, FindPetsResponse, FindPetsError, AddPetData, AddPetResponse, AddPetError, GetNotDefinedResponse, GetNotDefinedError, PostNotDefinedResponse, PostNotDefinedError, FindPetByIdData, FindPetByIdResponse, FindPetByIdError, DeletePetData, DeletePetResponse, DeletePetError, FindPaginatedPetsData, FindPaginatedPetsResponse, FindPaginatedPetsError } from "../requests/types.gen"; export const useFindPets = = unknown[]>(clientOptions: Options = {}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useQuery({ queryKey: Common.UseFindPetsKeyFn(clientOptions, queryKey), queryFn: () => findPets({ ...clientOptions }).then(response => response.data as TData) as TData, ...options }); @@ -161,13 +161,13 @@ exports[`createSource > createSource - @hey-api/client-fetch 4`] = ` import * as Common from "./common"; import { type Options } from "@hey-api/client-fetch"; -import { type QueryClient, useQuery, useSuspenseQuery, useMutation, UseQueryResult, UseQueryOptions, UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; +import { type QueryClient, useQuery, useSuspenseQuery, useMutation, UseQueryResult, UseQueryOptions, UseMutationOptions, UseMutationResult, UseSuspenseQueryOptions } from "@tanstack/react-query"; import { client, findPets, addPet, getNotDefined, postNotDefined, findPetById, deletePet, findPaginatedPets } from "../requests/services.gen"; import { Pet, NewPet, Error, FindPetsData, FindPetsResponse, FindPetsError, AddPetData, AddPetResponse, AddPetError, GetNotDefinedResponse, GetNotDefinedError, PostNotDefinedResponse, PostNotDefinedError, FindPetByIdData, FindPetByIdResponse, FindPetByIdError, DeletePetData, DeletePetResponse, DeletePetError, FindPaginatedPetsData, FindPaginatedPetsResponse, FindPaginatedPetsError } from "../requests/types.gen"; -export const useFindPetsSuspense = = unknown[]>(clientOptions: Options = {}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseFindPetsKeyFn(clientOptions, queryKey), queryFn: () => findPets({ ...clientOptions }).then(response => response.data as TData) as TData, ...options }); -export const useGetNotDefinedSuspense = = unknown[]>(clientOptions: Options = {}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseGetNotDefinedKeyFn(clientOptions, queryKey), queryFn: () => getNotDefined({ ...clientOptions }).then(response => response.data as TData) as TData, ...options }); -export const useFindPetByIdSuspense = = unknown[]>(clientOptions: Options, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseFindPetByIdKeyFn(clientOptions, queryKey), queryFn: () => findPetById({ ...clientOptions }).then(response => response.data as TData) as TData, ...options }); -export const useFindPaginatedPetsSuspense = = unknown[]>(clientOptions: Options = {}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseFindPaginatedPetsKeyFn(clientOptions, queryKey), queryFn: () => findPaginatedPets({ ...clientOptions }).then(response => response.data as TData) as TData, ...options }); +export const useFindPetsSuspense = , TError = FindPetsError, TQueryKey extends Array = unknown[]>(clientOptions: Options = {}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseFindPetsKeyFn(clientOptions, queryKey), queryFn: () => findPets({ ...clientOptions }).then(response => response.data as TData) as TData, ...options }); +export const useGetNotDefinedSuspense = , TError = GetNotDefinedError, TQueryKey extends Array = unknown[]>(clientOptions: Options = {}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseGetNotDefinedKeyFn(clientOptions, queryKey), queryFn: () => getNotDefined({ ...clientOptions }).then(response => response.data as TData) as TData, ...options }); +export const useFindPetByIdSuspense = , TError = FindPetByIdError, TQueryKey extends Array = unknown[]>(clientOptions: Options, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseFindPetByIdKeyFn(clientOptions, queryKey), queryFn: () => findPetById({ ...clientOptions }).then(response => response.data as TData) as TData, ...options }); +export const useFindPaginatedPetsSuspense = , TError = FindPaginatedPetsError, TQueryKey extends Array = unknown[]>(clientOptions: Options = {}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseFindPaginatedPetsKeyFn(clientOptions, queryKey), queryFn: () => findPaginatedPets({ ...clientOptions }).then(response => response.data as TData) as TData, ...options }); " `; @@ -176,7 +176,7 @@ exports[`createSource > createSource - @hey-api/client-fetch 5`] = ` import * as Common from "./common"; import { type Options } from "@hey-api/client-fetch"; -import { type QueryClient, useQuery, useSuspenseQuery, useMutation, UseQueryResult, UseQueryOptions, UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; +import { type QueryClient, useQuery, useSuspenseQuery, useMutation, UseQueryResult, UseQueryOptions, UseMutationOptions, UseMutationResult, UseSuspenseQueryOptions } from "@tanstack/react-query"; import { client, findPets, addPet, getNotDefined, postNotDefined, findPetById, deletePet, findPaginatedPets } from "../requests/services.gen"; import { Pet, NewPet, Error, FindPetsData, FindPetsResponse, FindPetsError, AddPetData, AddPetResponse, AddPetError, GetNotDefinedResponse, GetNotDefinedError, PostNotDefinedResponse, PostNotDefinedError, FindPetByIdData, FindPetByIdResponse, FindPetByIdError, DeletePetData, DeletePetResponse, DeletePetError, FindPaginatedPetsData, FindPaginatedPetsResponse, FindPaginatedPetsError } from "../requests/types.gen"; export const prefetchUseFindPets = (queryClient: QueryClient, clientOptions: Options = {}) => queryClient.prefetchQuery({ queryKey: Common.UseFindPetsKeyFn(clientOptions), queryFn: () => findPets({ ...clientOptions }).then(response => response.data) }); diff --git a/tests/__snapshots__/generate.test.ts.snap b/tests/__snapshots__/generate.test.ts.snap index 082275f..233a8b0 100644 --- a/tests/__snapshots__/generate.test.ts.snap +++ b/tests/__snapshots__/generate.test.ts.snap @@ -416,7 +416,10 @@ exports[`generate > suspense.ts 1`] = ` "// generated with @7nohe/openapi-react-query-codegen@1.0.0 import type { Options } from "@hey-api/client-fetch"; -import { type UseQueryOptions, useSuspenseQuery } from "@tanstack/react-query"; +import { + useSuspenseQuery, + type UseSuspenseQueryOptions, +} from "@tanstack/react-query"; import { findPaginatedPets, findPetById, @@ -434,13 +437,16 @@ import type { } from "../requests/types.gen"; import * as Common from "./common"; export const useFindPetsSuspense = < - TData = Common.FindPetsDefaultResponse, + TData = NonNullable, TError = FindPetsError, TQueryKey extends Array = unknown[], >( clientOptions: Options = {}, queryKey?: TQueryKey, - options?: Omit, "queryKey" | "queryFn">, + options?: Omit< + UseSuspenseQueryOptions, + "queryKey" | "queryFn" + >, ) => useSuspenseQuery({ queryKey: Common.UseFindPetsKeyFn(clientOptions, queryKey), @@ -451,13 +457,16 @@ export const useFindPetsSuspense = < ...options, }); export const useGetNotDefinedSuspense = < - TData = Common.GetNotDefinedDefaultResponse, + TData = NonNullable, TError = GetNotDefinedError, TQueryKey extends Array = unknown[], >( clientOptions: Options = {}, queryKey?: TQueryKey, - options?: Omit, "queryKey" | "queryFn">, + options?: Omit< + UseSuspenseQueryOptions, + "queryKey" | "queryFn" + >, ) => useSuspenseQuery({ queryKey: Common.UseGetNotDefinedKeyFn(clientOptions, queryKey), @@ -468,13 +477,16 @@ export const useGetNotDefinedSuspense = < ...options, }); export const useFindPetByIdSuspense = < - TData = Common.FindPetByIdDefaultResponse, + TData = NonNullable, TError = FindPetByIdError, TQueryKey extends Array = unknown[], >( clientOptions: Options, queryKey?: TQueryKey, - options?: Omit, "queryKey" | "queryFn">, + options?: Omit< + UseSuspenseQueryOptions, + "queryKey" | "queryFn" + >, ) => useSuspenseQuery({ queryKey: Common.UseFindPetByIdKeyFn(clientOptions, queryKey), @@ -485,13 +497,16 @@ export const useFindPetByIdSuspense = < ...options, }); export const useFindPaginatedPetsSuspense = < - TData = Common.FindPaginatedPetsDefaultResponse, + TData = NonNullable, TError = FindPaginatedPetsError, TQueryKey extends Array = unknown[], >( clientOptions: Options = {}, queryKey?: TQueryKey, - options?: Omit, "queryKey" | "queryFn">, + options?: Omit< + UseSuspenseQueryOptions, + "queryKey" | "queryFn" + >, ) => useSuspenseQuery({ queryKey: Common.UseFindPaginatedPetsKeyFn(clientOptions, queryKey),