Skip to content

Commit 59dfcd2

Browse files
authored
add types for manually typing hook results in userland code (#2276)
1 parent 7dcab4e commit 59dfcd2

File tree

3 files changed

+130
-4
lines changed

3 files changed

+130
-4
lines changed

packages/toolkit/src/query/react/buildHooks.ts

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import { useStableQueryArgs } from './useSerializedStableValue'
5252
import type { UninitializedValue } from './constants'
5353
import { UNINITIALIZED_VALUE } from './constants'
5454
import { useShallowStableValue } from './useShallowStableValue'
55+
import type { BaseQueryFn } from '../baseQueryTypes'
5556

5657
// Copy-pasted from React-Redux
5758
export const useIsomorphicLayoutEffect =
@@ -97,7 +98,26 @@ export type UseQuery<D extends QueryDefinition<any, any, any, any>> = <
9798
>(
9899
arg: QueryArgFrom<D> | SkipToken,
99100
options?: UseQuerySubscriptionOptions & UseQueryStateOptions<D, R>
100-
) => UseQueryStateResult<D, R> & ReturnType<UseQuerySubscription<D>>
101+
) => UseQueryHookResult<D, R>
102+
103+
export type UseQueryHookResult<
104+
D extends QueryDefinition<any, any, any, any>,
105+
R = UseQueryStateDefaultResult<D>
106+
> = UseQueryStateResult<D, R> & UseQuerySubscriptionResult<D>
107+
108+
/**
109+
* Helper type to manually type the result
110+
* of the `useQuery` hook in userland code.
111+
*/
112+
export type TypedUseQueryHookResult<
113+
ResultType,
114+
QueryArg,
115+
BaseQuery extends BaseQueryFn,
116+
R = UseQueryStateDefaultResult<
117+
QueryDefinition<QueryArg, BaseQuery, string, ResultType, string>
118+
>
119+
> = TypedUseQueryStateResult<ResultType, QueryArg, BaseQuery, R> &
120+
TypedUseQuerySubscriptionResult<ResultType, QueryArg, BaseQuery>
101121

102122
interface UseQuerySubscriptionOptions extends SubscriptionOptions {
103123
/**
@@ -162,7 +182,23 @@ export type UseQuerySubscription<
162182
> = (
163183
arg: QueryArgFrom<D> | SkipToken,
164184
options?: UseQuerySubscriptionOptions
165-
) => Pick<QueryActionCreatorResult<D>, 'refetch'>
185+
) => UseQuerySubscriptionResult<D>
186+
187+
export type UseQuerySubscriptionResult<
188+
D extends QueryDefinition<any, any, any, any>
189+
> = Pick<QueryActionCreatorResult<D>, 'refetch'>
190+
191+
/**
192+
* Helper type to manually type the result
193+
* of the `useQuerySubscription` hook in userland code.
194+
*/
195+
export type TypedUseQuerySubscriptionResult<
196+
ResultType,
197+
QueryArg,
198+
BaseQuery extends BaseQueryFn
199+
> = UseQuerySubscriptionResult<
200+
QueryDefinition<QueryArg, BaseQuery, string, ResultType, string>
201+
>
166202

167203
export type UseLazyQueryLastPromiseInfo<
168204
D extends QueryDefinition<any, any, any, any>
@@ -338,6 +374,19 @@ export type UseQueryStateResult<
338374
R
339375
> = NoInfer<R>
340376

377+
/**
378+
* Helper type to manually type the result
379+
* of the `useQueryState` hook in userland code.
380+
*/
381+
export type TypedUseQueryStateResult<
382+
ResultType,
383+
QueryArg,
384+
BaseQuery extends BaseQueryFn,
385+
R = UseQueryStateDefaultResult<
386+
QueryDefinition<QueryArg, BaseQuery, string, ResultType, string>
387+
>
388+
> = NoInfer<R>
389+
341390
type UseQueryStateBaseResult<D extends QueryDefinition<any, any, any, any>> =
342391
QuerySubState<D> & {
343392
/**
@@ -435,6 +484,22 @@ export type UseMutationStateResult<
435484
reset: () => void
436485
}
437486

487+
/**
488+
* Helper type to manually type the result
489+
* of the `useMutation` hook in userland code.
490+
*/
491+
export type TypedUseMutationResult<
492+
ResultType,
493+
QueryArg,
494+
BaseQuery extends BaseQueryFn,
495+
R = MutationResultSelectorResult<
496+
MutationDefinition<QueryArg, BaseQuery, string, ResultType, string>
497+
>
498+
> = UseMutationStateResult<
499+
MutationDefinition<QueryArg, BaseQuery, string, ResultType, string>,
500+
R
501+
>
502+
438503
/**
439504
* A React hook that lets you trigger an update request for a given endpoint, and subscribes the component to read the request status from the Redux store. The component will re-render as the loading status changes.
440505
*

packages/toolkit/src/query/react/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,10 @@ const createApi = /* @__PURE__ */ buildCreateApi(
2121
reactHooksModule()
2222
)
2323

24+
export type {
25+
TypedUseQueryHookResult,
26+
TypedUseQueryStateResult,
27+
TypedUseQuerySubscriptionResult,
28+
TypedUseMutationResult,
29+
} from './buildHooks'
2430
export { createApi, reactHooksModule }

packages/toolkit/src/query/tests/unionTypes.test.ts

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
import type { SerializedError } from '@reduxjs/toolkit'
2-
import type { FetchBaseQueryError } from '@reduxjs/toolkit/query/react'
2+
import type {
3+
FetchBaseQueryError,
4+
TypedUseQueryHookResult,
5+
TypedUseQueryStateResult,
6+
TypedUseQuerySubscriptionResult,
7+
TypedUseMutationResult,
8+
} from '@reduxjs/toolkit/query/react'
39
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
410
import { expectExactType, expectType } from './helpers'
511

12+
const baseQuery = fetchBaseQuery()
613
const api = createApi({
7-
baseQuery: fetchBaseQuery(),
14+
baseQuery,
815
endpoints: (build) => ({
916
test: build.query<string, void>({ query: () => '' }),
1017
mutation: build.mutation<string, void>({ query: () => '' }),
@@ -547,4 +554,52 @@ describe.skip('TS only tests', () => {
547554
expectType<never>(result)
548555
}
549556
})
557+
558+
test('"Typed" helper types', () => {
559+
// useQuery
560+
{
561+
const result = api.endpoints.test.useQuery()
562+
expectType<TypedUseQueryHookResult<string, void, typeof baseQuery>>(
563+
result
564+
)
565+
}
566+
// useQuery with selectFromResult
567+
{
568+
const result = api.endpoints.test.useQuery(undefined, {
569+
selectFromResult: () => ({ x: true }),
570+
})
571+
expectType<
572+
TypedUseQueryHookResult<string, void, typeof baseQuery, { x: boolean }>
573+
>(result)
574+
}
575+
// useQueryState
576+
{
577+
const result = api.endpoints.test.useQueryState()
578+
expectType<TypedUseQueryStateResult<string, void, typeof baseQuery>>(
579+
result
580+
)
581+
}
582+
// useQueryState with selectFromResult
583+
{
584+
const result = api.endpoints.test.useQueryState(undefined, {
585+
selectFromResult: () => ({ x: true }),
586+
})
587+
expectType<
588+
TypedUseQueryStateResult<string, void, typeof baseQuery, { x: boolean }>
589+
>(result)
590+
}
591+
// useQuerySubscription
592+
{
593+
const result = api.endpoints.test.useQuerySubscription()
594+
expectType<
595+
TypedUseQuerySubscriptionResult<string, void, typeof baseQuery>
596+
>(result)
597+
}
598+
599+
// useMutation
600+
{
601+
const [trigger, result] = api.endpoints.mutation.useMutation()
602+
expectType<TypedUseMutationResult<string, void, typeof baseQuery>>(result)
603+
}
604+
})
550605
})

0 commit comments

Comments
 (0)