Skip to content

Commit 30c44e5

Browse files
committed
add inference of raw result type from schema (or specifying from a third type parameter)
1 parent 802c476 commit 30c44e5

File tree

4 files changed

+79
-25
lines changed

4 files changed

+79
-25
lines changed

packages/toolkit/src/query/core/apiState.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ export type MutationKeys<Definitions extends EndpointDefinitions> = {
184184
}[keyof Definitions]
185185

186186
type BaseQuerySubState<
187-
D extends BaseEndpointDefinition<any, any, any>,
187+
D extends BaseEndpointDefinition<any, any, any, any>,
188188
DataType = ResultTypeFrom<D>,
189189
> = {
190190
/**
@@ -222,7 +222,7 @@ type BaseQuerySubState<
222222
}
223223

224224
export type QuerySubState<
225-
D extends BaseEndpointDefinition<any, any, any>,
225+
D extends BaseEndpointDefinition<any, any, any, any>,
226226
DataType = ResultTypeFrom<D>,
227227
> = Id<
228228
| ({
@@ -252,15 +252,17 @@ export type QuerySubState<
252252
export type InfiniteQueryDirection = 'forward' | 'backward'
253253

254254
export type InfiniteQuerySubState<
255-
D extends BaseEndpointDefinition<any, any, any>,
255+
D extends BaseEndpointDefinition<any, any, any, any>,
256256
> =
257257
D extends InfiniteQueryDefinition<any, any, any, any, any>
258258
? QuerySubState<D, InfiniteData<ResultTypeFrom<D>, PageParamFrom<D>>> & {
259259
direction?: InfiniteQueryDirection
260260
}
261261
: never
262262

263-
type BaseMutationSubState<D extends BaseEndpointDefinition<any, any, any>> = {
263+
type BaseMutationSubState<
264+
D extends BaseEndpointDefinition<any, any, any, any>,
265+
> = {
264266
requestId: string
265267
data?: ResultTypeFrom<D>
266268
error?:
@@ -273,7 +275,9 @@ type BaseMutationSubState<D extends BaseEndpointDefinition<any, any, any>> = {
273275
fulfilledTimeStamp?: number
274276
}
275277

276-
export type MutationSubState<D extends BaseEndpointDefinition<any, any, any>> =
278+
export type MutationSubState<
279+
D extends BaseEndpointDefinition<any, any, any, any>,
280+
> =
277281
| (({
278282
status: QueryStatus.fulfilled
279283
} & WithRequiredProp<

packages/toolkit/src/query/core/buildMiddleware/cacheLifecycle.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import type { ThunkDispatch, UnknownAction } from '@reduxjs/toolkit'
2-
import type { BaseQueryFn, BaseQueryMeta } from '../../baseQueryTypes'
2+
import type {
3+
BaseQueryFn,
4+
BaseQueryMeta,
5+
BaseQueryResult,
6+
} from '../../baseQueryTypes'
37
import type { BaseEndpointDefinition } from '../../endpointDefinitions'
48
import { DefinitionType } from '../../endpointDefinitions'
59
import type { QueryCacheKey, RootState } from '../apiState'
@@ -32,7 +36,8 @@ export interface QueryBaseLifecycleApi<
3236
{ type: DefinitionType.query } & BaseEndpointDefinition<
3337
QueryArg,
3438
BaseQuery,
35-
ResultType
39+
ResultType,
40+
BaseQueryResult<BaseQuery>
3641
>
3742
>
3843
/**
@@ -55,7 +60,8 @@ export type MutationBaseLifecycleApi<
5560
{ type: DefinitionType.mutation } & BaseEndpointDefinition<
5661
QueryArg,
5762
BaseQuery,
58-
ResultType
63+
ResultType,
64+
BaseQueryResult<BaseQuery>
5965
>
6066
>
6167
}

packages/toolkit/src/query/endpointDefinitions.ts

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ type EndpointDefinitionWithQuery<
4646
QueryArg,
4747
BaseQuery extends BaseQueryFn,
4848
ResultType,
49+
RawResultType extends BaseQueryResult<BaseQuery>,
4950
> = {
5051
/**
5152
* `query` can be a function that returns either a `string` or an `object` which is passed to your `baseQuery`. If you are using [fetchBaseQuery](./fetchBaseQuery), this can return either a `string` or an `object` of properties in `FetchArgs`. If you use your own custom [`baseQuery`](../../rtk-query/usage/customizing-queries), you can customize this behavior to your liking.
@@ -91,7 +92,7 @@ type EndpointDefinitionWithQuery<
9192
* A function to manipulate the data returned by a query or mutation.
9293
*/
9394
transformResponse?(
94-
baseQueryReturnValue: BaseQueryResult<BaseQuery>,
95+
baseQueryReturnValue: RawResultType,
9596
meta: BaseQueryMeta<BaseQuery>,
9697
arg: QueryArg,
9798
): ResultType | Promise<ResultType>
@@ -105,7 +106,7 @@ type EndpointDefinitionWithQuery<
105106
): unknown
106107

107108
/** A schema for the result *before* it's passed to `transformResponse` */
108-
rawResponseSchema?: StandardSchemaV1<BaseQueryResult<BaseQuery>>
109+
rawResponseSchema?: StandardSchemaV1<RawResultType>
109110

110111
/** A schema for the error object returned by the `query` or `queryFn`, *before* it's passed to `transformErrorResponse` */
111112
rawErrorResponseSchema?: StandardSchemaV1<BaseQueryError<BaseQuery>>
@@ -183,10 +184,16 @@ export type BaseEndpointDefinition<
183184
QueryArg,
184185
BaseQuery extends BaseQueryFn,
185186
ResultType,
187+
RawResultType extends BaseQueryResult<BaseQuery> = BaseQueryResult<BaseQuery>,
186188
> = (
187189
| ([CastAny<BaseQueryResult<BaseQuery>, {}>] extends [NEVER]
188190
? never
189-
: EndpointDefinitionWithQuery<QueryArg, BaseQuery, ResultType>)
191+
: EndpointDefinitionWithQuery<
192+
QueryArg,
193+
BaseQuery,
194+
ResultType,
195+
RawResultType
196+
>)
190197
| EndpointDefinitionWithQueryFn<QueryArg, BaseQuery, ResultType>
191198
) & {
192199
/** A schema for the arguments to be passed to the `query` or `queryFn` */
@@ -550,7 +557,8 @@ export type QueryDefinition<
550557
TagTypes extends string,
551558
ResultType,
552559
ReducerPath extends string = string,
553-
> = BaseEndpointDefinition<QueryArg, BaseQuery, ResultType> &
560+
RawResultType extends BaseQueryResult<BaseQuery> = BaseQueryResult<BaseQuery>,
561+
> = BaseEndpointDefinition<QueryArg, BaseQuery, ResultType, RawResultType> &
554562
QueryExtraOptions<TagTypes, ResultType, QueryArg, BaseQuery, ReducerPath>
555563

556564
export interface InfiniteQueryTypes<
@@ -743,12 +751,14 @@ export type InfiniteQueryDefinition<
743751
TagTypes extends string,
744752
ResultType,
745753
ReducerPath extends string = string,
754+
RawResultType extends BaseQueryResult<BaseQuery> = BaseQueryResult<BaseQuery>,
746755
> =
747756
// Infinite query endpoints receive `{queryArg, pageParam}`
748757
BaseEndpointDefinition<
749758
InfiniteQueryCombinedArg<QueryArg, PageParam>,
750759
BaseQuery,
751-
ResultType
760+
ResultType,
761+
RawResultType
752762
> &
753763
InfiniteQueryExtraOptions<
754764
TagTypes,
@@ -876,7 +886,8 @@ export type MutationDefinition<
876886
TagTypes extends string,
877887
ResultType,
878888
ReducerPath extends string = string,
879-
> = BaseEndpointDefinition<QueryArg, BaseQuery, ResultType> &
889+
RawResultType extends BaseQueryResult<BaseQuery> = BaseQueryResult<BaseQuery>,
890+
> = BaseEndpointDefinition<QueryArg, BaseQuery, ResultType, RawResultType> &
880891
MutationExtraOptions<TagTypes, ResultType, QueryArg, BaseQuery, ReducerPath>
881892

882893
export type EndpointDefinition<
@@ -952,9 +963,21 @@ export type EndpointBuilder<
952963
*});
953964
*```
954965
*/
955-
query<ResultType, QueryArg>(
966+
query<
967+
ResultType,
968+
QueryArg,
969+
RawResultType extends
970+
BaseQueryResult<BaseQuery> = BaseQueryResult<BaseQuery>,
971+
>(
956972
definition: OmitFromUnion<
957-
QueryDefinition<QueryArg, BaseQuery, TagTypes, ResultType, ReducerPath>,
973+
QueryDefinition<
974+
QueryArg,
975+
BaseQuery,
976+
TagTypes,
977+
ResultType,
978+
ReducerPath,
979+
RawResultType
980+
>,
958981
'type'
959982
>,
960983
): QueryDefinition<QueryArg, BaseQuery, TagTypes, ResultType, ReducerPath>
@@ -984,14 +1007,20 @@ export type EndpointBuilder<
9841007
* });
9851008
* ```
9861009
*/
987-
mutation<ResultType, QueryArg>(
1010+
mutation<
1011+
ResultType,
1012+
QueryArg,
1013+
RawResultType extends
1014+
BaseQueryResult<BaseQuery> = BaseQueryResult<BaseQuery>,
1015+
>(
9881016
definition: OmitFromUnion<
9891017
MutationDefinition<
9901018
QueryArg,
9911019
BaseQuery,
9921020
TagTypes,
9931021
ResultType,
994-
ReducerPath
1022+
ReducerPath,
1023+
RawResultType
9951024
>,
9961025
'type'
9971026
>,
@@ -1058,18 +1087,19 @@ export function expandTagDescription(
10581087
return typeof description === 'string' ? { type: description } : description
10591088
}
10601089

1061-
export type QueryArgFrom<D extends BaseEndpointDefinition<any, any, any>> =
1062-
D extends BaseEndpointDefinition<infer QA, any, any> ? QA : never
1090+
export type QueryArgFrom<D extends BaseEndpointDefinition<any, any, any, any>> =
1091+
D extends BaseEndpointDefinition<infer QA, any, any, any> ? QA : never
10631092

10641093
// Just extracting `QueryArg` from `BaseEndpointDefinition`
10651094
// doesn't sufficiently match here.
10661095
// We need to explicitly match against `InfiniteQueryDefinition`
10671096
export type InfiniteQueryArgFrom<
1068-
D extends BaseEndpointDefinition<any, any, any>,
1097+
D extends BaseEndpointDefinition<any, any, any, any>,
10691098
> = D extends InfiniteQueryDefinition<infer QA, any, any, any, any> ? QA : never
10701099

1071-
export type ResultTypeFrom<D extends BaseEndpointDefinition<any, any, any>> =
1072-
D extends BaseEndpointDefinition<any, any, infer RT> ? RT : unknown
1100+
export type ResultTypeFrom<
1101+
D extends BaseEndpointDefinition<any, any, any, any>,
1102+
> = D extends BaseEndpointDefinition<any, any, infer RT, any> ? RT : unknown
10731103

10741104
export type ReducerPathFrom<
10751105
D extends EndpointDefinition<any, any, any, any, any>,

packages/toolkit/src/query/tests/createApi.test-d.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { setupApiStore } from '@internal/tests/utils/helpers'
2-
import type { SerializedError } from '@reduxjs/toolkit'
3-
import { configureStore } from '@reduxjs/toolkit'
2+
import type { EntityState, SerializedError } from '@reduxjs/toolkit'
3+
import { configureStore, createEntityAdapter } from '@reduxjs/toolkit'
44
import type {
55
DefinitionsFromApi,
66
FetchBaseQueryError,
@@ -467,6 +467,7 @@ describe('type tests', () => {
467467
})
468468
})
469469
test('schemas as a source of inference', () => {
470+
const postAdapter = createEntityAdapter<Post>()
470471
const api = createApi({
471472
baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
472473
endpoints: (build) => ({
@@ -482,6 +483,14 @@ describe('type tests', () => {
482483
argSchema,
483484
responseSchema: postSchema,
484485
}),
486+
query3: build.query({
487+
query: (_arg: void) => `/posts`,
488+
rawResponseSchema: v.array(postSchema),
489+
transformResponse: (posts) => {
490+
expectTypeOf(posts).toEqualTypeOf<Post[]>()
491+
return postAdapter.getInitialState(undefined, posts)
492+
},
493+
}),
485494
}),
486495
})
487496

@@ -496,6 +505,11 @@ describe('type tests', () => {
496505
expectTypeOf(
497506
api.endpoints.query2.Types.ResultType,
498507
).toEqualTypeOf<Post>()
508+
509+
expectTypeOf(api.endpoints.query3.Types.QueryArg).toEqualTypeOf<void>()
510+
expectTypeOf(api.endpoints.query3.Types.ResultType).toEqualTypeOf<
511+
EntityState<Post, Post['id']>
512+
>()
499513
})
500514
})
501515
})

0 commit comments

Comments
 (0)