Skip to content

Commit 6d630b0

Browse files
committed
add respect for select
1 parent 16bed21 commit 6d630b0

File tree

5 files changed

+213
-24
lines changed

5 files changed

+213
-24
lines changed

packages/query-core/src/__tests__/queryClient.test-d.tsx

Lines changed: 124 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
EnsureQueryDataOptions,
1111
FetchInfiniteQueryOptions,
1212
InfiniteData,
13+
InfiniteQueryExecuteOptions,
1314
MutationOptions,
1415
OmitKeyof,
1516
QueryKey,
@@ -157,24 +158,55 @@ describe('getQueryState', () => {
157158
})
158159
})
159160

161+
describe('fetchQuery', () => {
162+
it('should not allow passing select option', () => {
163+
assertType<Parameters<QueryClient['fetchQuery']>>([
164+
{
165+
queryKey: ['key'],
166+
queryFn: () => Promise.resolve('string'),
167+
// @ts-expect-error `select` is not supported on fetchQuery options
168+
select: (data: string) => data.length,
169+
},
170+
])
171+
})
172+
})
173+
160174
describe('fetchInfiniteQuery', () => {
175+
it('should not allow passing select option', () => {
176+
assertType<Parameters<QueryClient['fetchInfiniteQuery']>>([
177+
{
178+
queryKey: ['key'],
179+
queryFn: () => Promise.resolve({ count: 1 }),
180+
initialPageParam: 1,
181+
getNextPageParam: () => 2,
182+
// @ts-expect-error `select` is not supported on fetchInfiniteQuery options
183+
select: (data) => ({
184+
pages: data.pages.map(
185+
(x: unknown) => `count: ${(x as { count: number }).count}`,
186+
),
187+
pageParams: data.pageParams,
188+
}),
189+
},
190+
])
191+
})
192+
161193
it('should allow passing pages', async () => {
162194
const data = await new QueryClient().fetchInfiniteQuery({
163195
queryKey: ['key'],
164-
queryFn: () => Promise.resolve('string'),
196+
queryFn: () => Promise.resolve({ count: 1 }),
165197
getNextPageParam: () => 1,
166198
initialPageParam: 1,
167199
pages: 5,
168200
})
169201

170-
expectTypeOf(data).toEqualTypeOf<InfiniteData<string, number>>()
202+
expectTypeOf(data).toEqualTypeOf<InfiniteData<{ count: number }, number>>()
171203
})
172204

173205
it('should not allow passing getNextPageParam without pages', () => {
174206
assertType<Parameters<QueryClient['fetchInfiniteQuery']>>([
175207
{
176208
queryKey: ['key'],
177-
queryFn: () => Promise.resolve('string'),
209+
queryFn: () => Promise.resolve({ count: 1 }),
178210
initialPageParam: 1,
179211
getNextPageParam: () => 1,
180212
},
@@ -183,6 +215,72 @@ describe('fetchInfiniteQuery', () => {
183215

184216
it('should not allow passing pages without getNextPageParam', () => {
185217
assertType<Parameters<QueryClient['fetchInfiniteQuery']>>([
218+
// @ts-expect-error Property 'getNextPageParam' is missing
219+
{
220+
queryKey: ['key'],
221+
queryFn: () => Promise.resolve({ count: 1 }),
222+
initialPageParam: 1,
223+
pages: 5,
224+
},
225+
])
226+
})
227+
})
228+
229+
describe('query', () => {
230+
it('should allow passing select option', () => {
231+
assertType<Parameters<QueryClient['query']>>([
232+
{
233+
queryKey: ['key'],
234+
queryFn: () => Promise.resolve('string'),
235+
select: (data) => (data as string).length,
236+
},
237+
])
238+
})
239+
})
240+
241+
describe('infiniteQuery', () => {
242+
it('should not allow passing select option', () => {
243+
assertType<Parameters<QueryClient['infiniteQuery']>>([
244+
{
245+
queryKey: ['key'],
246+
queryFn: () => Promise.resolve({ count: 1 }),
247+
initialPageParam: 1,
248+
getNextPageParam: () => 2,
249+
select: (data) => ({
250+
pages: data.pages.map(
251+
(x) => `count: ${(x as { count: number }).count}`,
252+
),
253+
pageParams: data.pageParams,
254+
}),
255+
},
256+
])
257+
})
258+
259+
it('should allow passing pages', async () => {
260+
const data = await new QueryClient().fetchInfiniteQuery({
261+
queryKey: ['key'],
262+
queryFn: () => Promise.resolve({ count: 1 }),
263+
getNextPageParam: () => 1,
264+
initialPageParam: 1,
265+
pages: 5,
266+
})
267+
268+
expectTypeOf(data).toEqualTypeOf<InfiniteData<{ count: number }, number>>()
269+
})
270+
271+
it('should not allow passing getNextPageParam without pages', () => {
272+
assertType<Parameters<QueryClient['infiniteQuery']>>([
273+
{
274+
queryKey: ['key'],
275+
queryFn: () => Promise.resolve({ count: 1 }),
276+
initialPageParam: 1,
277+
getNextPageParam: () => 1,
278+
},
279+
])
280+
})
281+
282+
it('should not allow passing pages without getNextPageParam', () => {
283+
assertType<Parameters<QueryClient['infiniteQuery']>>([
186284
// @ts-expect-error Property 'getNextPageParam' is missing
187285
{
188286
queryKey: ['key'],
@@ -227,6 +325,22 @@ describe('fully typed usage', () => {
227325
// Construct typed arguments
228326
//
229327

328+
const infiniteQueryOptions: InfiniteQueryExecuteOptions<TData, TError> = {
329+
queryKey: ['key'] as any,
330+
pages: 5,
331+
getNextPageParam: (lastPage) => {
332+
expectTypeOf(lastPage).toEqualTypeOf<TData>()
333+
return 0
334+
},
335+
initialPageParam: 0,
336+
select: (data) => {
337+
expectTypeOf(data).toEqualTypeOf<InfiniteData<TData, unknown>>()
338+
return data
339+
},
340+
}
341+
342+
const infiniteQueryOptions
343+
230344
const queryOptions: EnsureQueryDataOptions<TData, TError> = {
231345
queryKey: ['key'] as any,
232346
}
@@ -240,6 +354,7 @@ describe('fully typed usage', () => {
240354
},
241355
initialPageParam: 0,
242356
}
357+
243358
const mutationOptions: MutationOptions<TData, TError> = {}
244359

245360
const queryFilters: QueryFilters<DataTag<QueryKey, TData, TError>> = {
@@ -323,7 +438,12 @@ describe('fully typed usage', () => {
323438
>()
324439

325440
const infiniteQuery = await queryClient.infiniteQuery(
326-
fetchInfiniteQueryOptions,
441+
infiniteQueryOptions,
442+
)
443+
expectTypeOf(infiniteQuery).toEqualTypeOf<InfiniteData<TData, unknown>>()
444+
445+
const infiniteQuery = await queryClient.infiniteQuery(
446+
infiniteQueryOptions,
327447
)
328448
expectTypeOf(infiniteQuery).toEqualTypeOf<InfiniteData<TData, unknown>>()
329449

packages/query-core/src/__tests__/queryClient.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -935,7 +935,7 @@ describe('queryClient', () => {
935935
Promise.resolve('data')
936936

937937
await expect(
938-
queryClient.query<StrictData, any, StrictData, StrictQueryKey>({
938+
queryClient.query<StrictData, any, StrictData, StrictData, StrictQueryKey>({
939939
queryKey: key,
940940
queryFn: fetchFn,
941941
}),
@@ -1433,7 +1433,7 @@ describe('queryClient', () => {
14331433
Promise.resolve('data')
14341434

14351435
await queryClient
1436-
.query<StrictData, any, StrictData, StrictQueryKey>({
1436+
.query<StrictData, any, StrictData, StrictData, StrictQueryKey>({
14371437
queryKey: key,
14381438
queryFn: fetchFn,
14391439
})

packages/query-core/src/queryClient.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import type {
2525
InferDataFromTag,
2626
InferErrorFromTag,
2727
InfiniteData,
28+
InfiniteQueryExecuteOptions,
2829
InvalidateOptions,
2930
InvalidateQueryFilters,
3031
MutationKey,
@@ -33,6 +34,7 @@ import type {
3334
NoInfer,
3435
OmitKeyof,
3536
QueryClientConfig,
37+
QueryExecuteOptions,
3638
QueryKey,
3739
QueryObserverOptions,
3840
QueryOptions,
@@ -333,17 +335,19 @@ export class QueryClient {
333335
return Promise.all(promises).then(noop)
334336
}
335337

336-
query<
338+
async query<
337339
TQueryFnData,
338340
TError = DefaultError,
339341
TData = TQueryFnData,
342+
TQueryData = TQueryFnData,
340343
TQueryKey extends QueryKey = QueryKey,
341344
TPageParam = never,
342345
>(
343-
options: FetchQueryOptions<
346+
options: QueryExecuteOptions<
344347
TQueryFnData,
345348
TError,
346349
TData,
350+
TQueryData,
347351
TQueryKey,
348352
TPageParam
349353
>,
@@ -357,11 +361,21 @@ export class QueryClient {
357361

358362
const query = this.#queryCache.build(this, defaultedOptions)
359363

360-
return query.isStaleByTime(
364+
const isStale = query.isStaleByTime(
361365
resolveStaleTime(defaultedOptions.staleTime, query),
362366
)
367+
368+
const basePromise = isStale
363369
? query.fetch(defaultedOptions)
364-
: Promise.resolve(query.state.data as TData)
370+
: Promise.resolve(query.state.data as TQueryData)
371+
372+
const select = defaultedOptions.select
373+
374+
if (select) {
375+
return basePromise.then((data) => select(data))
376+
}
377+
378+
return basePromise.then((data) => data as unknown as TData)
365379
}
366380

367381
fetchQuery<
@@ -399,7 +413,7 @@ export class QueryClient {
399413
TQueryKey extends QueryKey = QueryKey,
400414
TPageParam = unknown,
401415
>(
402-
options: FetchInfiniteQueryOptions<
416+
options: InfiniteQueryExecuteOptions<
403417
TQueryFnData,
404418
TError,
405419
TData,
@@ -415,6 +429,7 @@ export class QueryClient {
415429
>(options.pages)
416430
return this.query(options as any)
417431
}
432+
418433
fetchInfiniteQuery<
419434
TQueryFnData,
420435
TError = DefaultError,

packages/query-core/src/types.ts

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -488,24 +488,37 @@ export type DefaultedInfiniteQueryObserverOptions<
488488
'throwOnError' | 'refetchOnReconnect' | 'queryHash'
489489
>
490490

491-
export interface FetchQueryOptions<
491+
export interface QueryExecuteOptions<
492492
TQueryFnData = unknown,
493493
TError = DefaultError,
494494
TData = TQueryFnData,
495+
TQueryData = TQueryFnData,
495496
TQueryKey extends QueryKey = QueryKey,
496497
TPageParam = never,
497498
> extends WithRequired<
498-
QueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>,
499+
QueryOptions<TQueryFnData, TError, TQueryData, TQueryKey, TPageParam>,
499500
'queryKey'
500501
> {
501502
initialPageParam?: never
503+
select?: (data: TQueryData) => TData
502504
/**
503505
* The time in milliseconds after data is considered stale.
504506
* If the data is fresh it will be returned from the cache.
505507
*/
506-
staleTime?: StaleTimeFunction<TQueryFnData, TError, TData, TQueryKey>
508+
staleTime?: StaleTimeFunction<TQueryFnData, TError, TQueryData, TQueryKey>
507509
}
508510

511+
export interface FetchQueryOptions<
512+
TQueryFnData = unknown,
513+
TError = DefaultError,
514+
TData = TQueryFnData,
515+
TQueryKey extends QueryKey = QueryKey,
516+
TPageParam = never,
517+
> extends Omit<
518+
QueryExecuteOptions<TQueryFnData, TError, TData, TData, TQueryKey, TPageParam>,
519+
'select'
520+
> {}
521+
509522
export interface EnsureQueryDataOptions<
510523
TQueryFnData = unknown,
511524
TError = DefaultError,
@@ -538,31 +551,52 @@ export type EnsureInfiniteQueryDataOptions<
538551
revalidateIfStale?: boolean
539552
}
540553

541-
type FetchInfiniteQueryPages<TQueryFnData = unknown, TPageParam = unknown> =
554+
type InfiniteQueryPages<TQueryFnData = unknown, TPageParam = unknown> =
542555
| { pages?: never }
543556
| {
544557
pages: number
545558
getNextPageParam: GetNextPageParamFunction<TPageParam, TQueryFnData>
546559
}
547560

548-
export type FetchInfiniteQueryOptions<
561+
export type InfiniteQueryExecuteOptions<
549562
TQueryFnData = unknown,
550563
TError = DefaultError,
551564
TData = TQueryFnData,
552565
TQueryKey extends QueryKey = QueryKey,
553566
TPageParam = unknown,
554567
> = Omit<
555-
FetchQueryOptions<
568+
QueryExecuteOptions<
556569
TQueryFnData,
557570
TError,
571+
TData,
558572
InfiniteData<TData, TPageParam>,
559573
TQueryKey,
560574
TPageParam
561575
>,
562576
'initialPageParam'
563577
> &
564578
InitialPageParam<TPageParam> &
565-
FetchInfiniteQueryPages<TQueryFnData, TPageParam>
579+
InfiniteQueryPages<TQueryFnData, TPageParam>
580+
581+
export type FetchInfiniteQueryOptions<
582+
TQueryFnData = unknown,
583+
TError = DefaultError,
584+
TData = TQueryFnData,
585+
TQueryKey extends QueryKey = QueryKey,
586+
TPageParam = unknown,
587+
> =
588+
Omit<
589+
FetchQueryOptions<
590+
TQueryFnData,
591+
TError,
592+
InfiniteData<TData, TPageParam>,
593+
TQueryKey,
594+
TPageParam
595+
>,
596+
'initialPageParam'
597+
> &
598+
InitialPageParam<TPageParam> &
599+
InfiniteQueryPages<TQueryFnData, TPageParam>
566600

567601
export interface ResultOptions {
568602
throwOnError?: boolean

0 commit comments

Comments
 (0)