diff --git a/packages/toolkit/src/query/react/buildHooks.ts b/packages/toolkit/src/query/react/buildHooks.ts index b6d611b778..d3a8581a8b 100644 --- a/packages/toolkit/src/query/react/buildHooks.ts +++ b/packages/toolkit/src/query/react/buildHooks.ts @@ -98,6 +98,12 @@ export type UseQuery> = < options?: UseQuerySubscriptionOptions & UseQueryStateOptions, ) => UseQueryHookResult +export type TypedUseQuery< + ResultType, + QueryArg, + BaseQuery extends BaseQueryFn, +> = UseQuery> + export type UseQueryHookResult< D extends QueryDefinition, R = UseQueryStateDefaultResult, @@ -182,6 +188,14 @@ export type UseQuerySubscription< options?: UseQuerySubscriptionOptions, ) => UseQuerySubscriptionResult +export type TypedUseQuerySubscription< + ResultType, + QueryArg, + BaseQuery extends BaseQueryFn, +> = UseQuerySubscription< + QueryDefinition +> + export type UseQuerySubscriptionResult< D extends QueryDefinition, > = Pick, 'refetch'> @@ -231,6 +245,14 @@ export type UseLazyQuery> = < UseLazyQueryLastPromiseInfo, ] +export type TypedUseLazyQuery< + ResultType, + QueryArg, + BaseQuery extends BaseQueryFn, +> = UseLazyQuery< + QueryDefinition +> + export type LazyQueryTrigger> = { /** * Triggers a lazy query. @@ -258,6 +280,14 @@ export type LazyQueryTrigger> = { ): QueryActionCreatorResult } +export type TypedLazyQueryTrigger< + ResultType, + QueryArg, + BaseQuery extends BaseQueryFn, +> = LazyQueryTrigger< + QueryDefinition +> + /** * A React hook similar to [`useQuerySubscription`](#usequerysubscription), but with manual control over when the data fetching occurs. * @@ -275,6 +305,14 @@ export type UseLazyQuerySubscription< options?: SubscriptionOptions, ) => readonly [LazyQueryTrigger, QueryArgFrom | UninitializedValue] +export type TypedUseLazyQuerySubscription< + ResultType, + QueryArg, + BaseQuery extends BaseQueryFn, +> = UseLazyQuerySubscription< + QueryDefinition +> + export type QueryStateSelector< R extends Record, D extends QueryDefinition, @@ -297,6 +335,14 @@ export type UseQueryState> = < options?: UseQueryStateOptions, ) => UseQueryStateResult +export type TypedUseQueryState< + ResultType, + QueryArg, + BaseQuery extends BaseQueryFn, +> = UseQueryState< + QueryDefinition +> + export type UseQueryStateOptions< D extends QueryDefinition, R extends Record, @@ -514,6 +560,14 @@ export type UseMutation> = < options?: UseMutationStateOptions, ) => readonly [MutationTrigger, UseMutationStateResult] +export type TypedUseMutation< + ResultType, + QueryArg, + BaseQuery extends BaseQueryFn, +> = UseMutation< + MutationDefinition +> + export type MutationTrigger> = { /** @@ -535,6 +589,14 @@ export type MutationTrigger> = (arg: QueryArgFrom): MutationActionCreatorResult } +export type TypedUseMutationTrigger< + ResultType, + QueryArg, + BaseQuery extends BaseQueryFn, +> = MutationTrigger< + MutationDefinition +> + /** * Wrapper around `defaultQueryStateSelector` to be used in `useQuery`. * We want the initial render to already come back with diff --git a/packages/toolkit/src/query/react/index.ts b/packages/toolkit/src/query/react/index.ts index b9acba6b6b..ba2815f123 100644 --- a/packages/toolkit/src/query/react/index.ts +++ b/packages/toolkit/src/query/react/index.ts @@ -18,5 +18,13 @@ export type { TypedUseQueryHookResult, TypedUseQueryStateResult, TypedUseQuerySubscriptionResult, + TypedLazyQueryTrigger, + TypedUseLazyQuery, + TypedUseMutation, + TypedUseMutationTrigger, + TypedUseQueryState, + TypedUseQuery, + TypedUseQuerySubscription, + TypedUseLazyQuerySubscription, } from './buildHooks' export { createApi, reactHooksModule, reactHooksModuleName } diff --git a/packages/toolkit/src/query/tests/buildHooks.test-d.tsx b/packages/toolkit/src/query/tests/buildHooks.test-d.tsx index 9e15bf68ee..25551c000c 100644 --- a/packages/toolkit/src/query/tests/buildHooks.test-d.tsx +++ b/packages/toolkit/src/query/tests/buildHooks.test-d.tsx @@ -193,7 +193,7 @@ describe('type tests', () => { } }) - test('selectFromResult (query) behaviors', () => { + test('top level named hooks', () => { interface Post { id: number name: string diff --git a/packages/toolkit/src/query/tests/unionTypes.test-d.ts b/packages/toolkit/src/query/tests/unionTypes.test-d.ts index d47cdae41b..2ab109dfa9 100644 --- a/packages/toolkit/src/query/tests/unionTypes.test-d.ts +++ b/packages/toolkit/src/query/tests/unionTypes.test-d.ts @@ -3,8 +3,16 @@ import type { FetchBaseQueryError, TypedUseMutationResult, TypedUseQueryHookResult, + TypedUseQueryState, TypedUseQueryStateResult, TypedUseQuerySubscriptionResult, + TypedLazyQueryTrigger, + TypedUseLazyQuery, + TypedUseLazyQuerySubscription, + TypedUseMutation, + TypedUseMutationTrigger, + TypedUseQuerySubscription, + TypedUseQuery, } from '@reduxjs/toolkit/query/react' import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' @@ -725,6 +733,10 @@ describe('union types', () => { describe('"Typed" helper types', () => { test('useQuery', () => { + expectTypeOf>().toMatchTypeOf( + api.endpoints.getTest.useQuery, + ) + const result = api.endpoints.getTest.useQuery() expectTypeOf< @@ -743,6 +755,10 @@ describe('"Typed" helper types', () => { }) test('useQueryState', () => { + expectTypeOf< + TypedUseQueryState + >().toMatchTypeOf(api.endpoints.getTest.useQueryState) + const result = api.endpoints.getTest.useQueryState() expectTypeOf< @@ -761,6 +777,10 @@ describe('"Typed" helper types', () => { }) test('useQuerySubscription', () => { + expectTypeOf< + TypedUseQuerySubscription + >().toMatchTypeOf(api.endpoints.getTest.useQuerySubscription) + const result = api.endpoints.getTest.useQuerySubscription() expectTypeOf< @@ -768,11 +788,101 @@ describe('"Typed" helper types', () => { >().toEqualTypeOf(result) }) + test('useLazyQuery', () => { + expectTypeOf< + TypedUseLazyQuery + >().toMatchTypeOf(api.endpoints.getTest.useLazyQuery) + + const [trigger, result] = api.endpoints.getTest.useLazyQuery() + + expectTypeOf< + TypedLazyQueryTrigger + >().toMatchTypeOf(trigger) + + expectTypeOf< + TypedUseQueryHookResult + >().toMatchTypeOf(result) + }) + + test('useLazyQuery with selectFromResult', () => { + const [trigger, result] = api.endpoints.getTest.useLazyQuery({ + selectFromResult: () => ({ x: true }), + }) + + expectTypeOf< + TypedLazyQueryTrigger + >().toMatchTypeOf(trigger) + + expectTypeOf< + TypedUseQueryHookResult + >().toMatchTypeOf(result) + }) + + test('useLazyQuerySubscription', () => { + expectTypeOf< + TypedUseLazyQuerySubscription + >().toMatchTypeOf(api.endpoints.getTest.useLazyQuerySubscription) + + const [trigger] = api.endpoints.getTest.useLazyQuerySubscription() + + expectTypeOf< + TypedLazyQueryTrigger + >().toMatchTypeOf(trigger) + }) + test('useMutation', () => { + expectTypeOf< + TypedUseMutation + >().toMatchTypeOf(api.endpoints.mutation.useMutation) + const [trigger, result] = api.endpoints.mutation.useMutation() + expectTypeOf< + TypedUseMutationTrigger + >().toMatchTypeOf(trigger) + expectTypeOf< TypedUseMutationResult >().toMatchTypeOf(result) }) + + test('useQuery - defining selectFromResult separately', () => { + const selectFromResult = ( + result: TypedUseQueryStateResult, + ) => ({ x: true }) + + const result = api.endpoints.getTest.useQuery(undefined, { + selectFromResult, + }) + + expectTypeOf(result).toEqualTypeOf< + TypedUseQueryHookResult< + string, + void, + typeof baseQuery, + ReturnType + > + >() + }) + + test('useMutation - defining selectFromResult separately', () => { + const selectFromResult = ( + result: Omit< + TypedUseMutationResult, + 'reset' | 'originalArgs' + >, + ) => ({ x: true }) + + const [trigger, result] = api.endpoints.mutation.useMutation({ + selectFromResult, + }) + expectTypeOf(result).toEqualTypeOf< + TypedUseMutationResult< + string, + void, + typeof baseQuery, + ReturnType + > + >() + }) })