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.
Changes
What does this PR change? Link to any related issue(s).
close #2458
How to Review
This PR closes a type-safety hole in useInfiniteQuery: required pagination options are now enforced at compile time and the pageParam type is correctly inferred from initialPageParam.
What changed (at a glance)
• Introduced InferPageParamType and threaded it into UseInfiniteQueryMethod so pageParam is inferred from options.initialPageParam.
• Required initialPageParam in the useInfiniteQuery options we expose.
• Plumbed initialPageParam through to the actual useInfiniteQuery call and removed the implicit pageParam = 0 default.
• Goal: make “forgetting getNextPageParam/initialPageParam” a TypeScript error, and restore proper inference for pageParam.
Files to open first
• Types where UseInfiniteQueryMethod is declared (look for InferPageParamType usage).
• createClient(...) implementation (look for useInfiniteQuery(...) and the extraction of initialPageParam).
⸻
Verification steps
Use any endpoint type; the shape here is illustrative.
type Page = { items: string[]; next?: string };
declare const api: ReturnType;
// ❌ Should FAIL: missing getNextPageParam
api.examples.list.useInfiniteQuery(
{ limit: 10 },
{
initialPageParam: undefined,
// @ts-expect-error getNextPageParam is required for infinite queries
}
);
// ❌ Should FAIL: wrong initialPageParam type
api.examples.list.useInfiniteQuery(
{ limit: 10 },
{
// @ts-expect-error initialPageParam must match pageParam type
initialPageParam: 0,
getNextPageParam: (last: Page) => last.next,
}
);
// ✅ Should PASS: both required options provided; pageParam is inferred as string | undefined
api.examples.list.useInfiniteQuery(
{ limit: 10 },
{
initialPageParam: undefined,
getNextPageParam: (last: Page) => last.next,
select: (data) => data, // keep an eye on select inference too
}
);
If the repo uses tsd, add two assertions:
// tsd
expectError(
useInfiniteQuery({ /* ... /, initialPageParam: undefined }) // missing getNextPageParam
);
expectAssignable(
useInfiniteQuery({ / ... */, initialPageParam: undefined, getNextPageParam: (p: Page) => p.next })
);
What to keep in mind
• pageParam in the queryFn context should now be inferred from initialPageParam.
• Omitting getNextPageParam should fail type-check.
• select should continue to infer correctly for InfiniteData.
Runtime sanity (manual)
• initialPageParam: undefined
• getNextPageParam: (last) => last.next
Expect: pagination works as before. No behavior regression from removing the pageParam = 0 default.
Backward compatibility
• Existing callers that already provided both initialPageParam and getNextPageParam continue to compile and run.
• Callers relying on the old implicit pageParam = 0 default will need to set an explicit initialPageParam—this is an intentional tightening for safety.
• Non-infinite useQuery paths are unaffected.
Code reading checklist
• InferPageParamType does not widen to unknown in common cases.
• useInfiniteQuery options intersection requires initialPageParam.
• The option set for useInfiniteQuery effectively requires getNextPageParam (no recursive type loosening).
• select and error/data generics remain intact (no any leakage).
• No runtime dead paths introduced; initialPageParam is passed through to TanStack.
Edge cases to spot-check
• Custom pageParamName still works with the stricter types.
• Non-cursor pagination (numeric pages) with initialPageParam: 1 and getNextPageParam: (last, pages) => pages.length + 1.
• previousPageParam flows (where applicable) still type-check when used.
⸻
Risks & mitigations
• Risk: Slight source break for callers who omitted initialPageParam/getNextPageParam.
Mitigation: compile-time error with clear fix; matches TanStack’s intended contract for infinite queries.
⸻
Reviewer tips
• Try intentionally removing getNextPageParam in a local example: it should fail the build.
• Hover pageParam inside the queryFn: it should be the exact type implied by initialPageParam (e.g., string | undefined, not unknown).
• Confirm no change to emitted JS other than threading initialPageParam.
⸻
Author note
I have this patch running locally. If there’s appetite, I can add tsd tests in this PR to lock the constraints in CI.
⸻
Checklist
docs/
updated (if necessary)pnpm run update:examples
run (only applicable for openapi-typescript)