fix: type infinite query options with a single page as TQueryFnData - #204
Merged
Conversation
The generated infinite hooks instantiated `UseInfiniteQueryOptions` with only two type arguments, `<TData, TError>`. TanStack Query v5's first slot is TQueryFnData — one page — not TData, and the hooks' TData defaults to `InfiniteData<...>`, so the aggregate landed in the page slot and `getNextPageParam` handed callers `InfiniteData<Page>` as `lastPage`. Overriding the pagination scheme therefore needed a cast. The options are now instantiated as `<Page, TError, TData>`, and the paginated queryFn resolves to that same page type. Only the first three type arguments are written: TanStack Query dropped TQueryData from `UseInfiniteQueryOptions` within the v5 line, so later positions are not stable across the `^5.0.0` peer range. Two adjacent gaps close with it: - `prefetchUse*Infinite` omitted `initialPageParam`/`getNextPageParam` without re-adding them as optional, so overrides were type-forbidden even though `...options` is spread after the defaults. - The `infiniteQueryOptions` factories took no options argument at all and could not be customised. They now accept the pagination overrides. Adds a type-level regression test to the react-app example, which CI typechecks after codegen. closes #203, closes #139
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Coverage Report
File Coverage
|
||||||||||||||||||||||||||||||||||||||||||||||||||
Exact<T, U> is satisfied by `any`, so the type assertions could have passed vacuously. A @ts-expect-error on a non-existent property errors as unused if the parameter is `any`, which makes the check definitive. Also exercises `pages` in the prefetch overrides: FetchInfiniteQueryOptions only accepts getNextPageParam on the union member that requires it, and that options type changed shape here.
Every caller computed getPageType(op) only to hand it straight back, so the parameter carried no information. Generated output is unchanged. Also covers overriding initialPageParam in the regression test: TanStack Query marks it required, so it has to be re-added as optional rather than merely omitted, and only getNextPageParam was exercised.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Generated infinite hooks instantiated the options type with only two type arguments:
TanStack Query v5's first slot is
TQueryFnData— a single page — notTData. Since the hooks'TDatadefaults toInfiniteData<...>, the aggregate landed in the page slot andgetNextPageParamhanded callersInfiniteData<Page>aslastPage:Runtime behaviour was already correct (
...optionsis spread after the defaults); this was purely a typing defect, but it made the documented override unusable without a cast.Fix
The options are instantiated as
<Page, TError, TData>, and the paginatedqueryFnresolves to that same page type so the two agree.PageisNonNullable<Common.XxxDefaultResponse>—throwOnError: truemeans a resolved page is always present, solastPageneeds no optional chaining.Only the first three type arguments are written. TanStack Query dropped
TQueryDatafromUseInfiniteQueryOptionswithin the v5 line (6 params in 5.59.13, 5 in 5.101.4), so positions beyondTDataare not stable across the^5.0.0peer range. Positions 1–3 are identical in 5.0.0, 5.59.13 and 5.101.4.Two adjacent gaps close with it:
prefetchUse*InfiniteomittedinitialPageParam/getNextPageParamwithout re-adding them as optional, so overrides were type-forbidden even though...optionsspreads last.getNextPageParamis spelled out rather thanPicked, becauseFetchInfiniteQueryOptionsonly exposes it on the union member that also requirespages.infiniteQueryOptionsfactories took nooptionsargument at all. They now accept the pagination overrides — deliberately just those, since the factory's return type is what every downstream consumer infers from and a wider options type (select,placeholderData, …) would make that inference ambiguous.The default
TDatais unchanged (InfiniteData<Common.XxxDefaultResponse>), so no existing result type shifts; onlyTQueryFnDataisNonNullable. The visible consequence is an asymmetry —lastPage.petsneeds no chaining whiledata.pages[0]?.petsstill does — kept deliberately to hold the diff to the defect.Verification
examples/react-app/src/infiniteQueryTypes.tsis a new type-level regression test covering all four surfaces (hook, suspense hook, prefetch includingpages, options factory). CI already runstsc --noEmitover the example after codegen. Reverting the generator change makes it fail with the issue's exactTS2339.Exact<T, U>helper rather than only checking that things compile, so the defaultTData, the explicit-TDataescape hatch, and the suspense result type are all asserted unchanged. SinceExact<>is also satisfied byany, a@ts-expect-erroron a non-existent property proveslastPageis genuinely the page type.@tanstack/react-query@5.59.13(what the examples pin) and@tanstack/react-query@5.101.4(latest), which is the claim the three-type-argument design rests on.react-app,nextjs-appandtanstack-router-appall regenerate and typecheck, andnext buildsucceeds.Not addressed:
data.pageParamsis stillunknown.TPageParamis not reachable at a version-stable type-argument position, and the issue lists this as a corroborating symptom rather than expected behaviour.closes #203, closes #139