Skip to content

Commit

Permalink
add types for manually typing hook results in userland code (#2276)
Browse files Browse the repository at this point in the history
  • Loading branch information
phryneas authored and markerikson committed Aug 11, 2022
1 parent d0048ab commit 2a66246
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 10 deletions.
69 changes: 67 additions & 2 deletions packages/toolkit/src/query/react/buildHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import { useStableQueryArgs } from './useSerializedStableValue'
import type { UninitializedValue } from './constants'
import { UNINITIALIZED_VALUE } from './constants'
import { useShallowStableValue } from './useShallowStableValue'
import type { BaseQueryFn } from '../baseQueryTypes'

// Copy-pasted from React-Redux
export const useIsomorphicLayoutEffect =
Expand Down Expand Up @@ -98,7 +99,26 @@ export type UseQuery<D extends QueryDefinition<any, any, any, any>> = <
>(
arg: QueryArgFrom<D> | SkipToken,
options?: UseQuerySubscriptionOptions & UseQueryStateOptions<D, R>
) => UseQueryStateResult<D, R> & ReturnType<UseQuerySubscription<D>>
) => UseQueryHookResult<D, R>

export type UseQueryHookResult<
D extends QueryDefinition<any, any, any, any>,
R = UseQueryStateDefaultResult<D>
> = UseQueryStateResult<D, R> & UseQuerySubscriptionResult<D>

/**
* Helper type to manually type the result
* of the `useQuery` hook in userland code.
*/
export type TypedUseQueryHookResult<
ResultType,
QueryArg,
BaseQuery extends BaseQueryFn,
R = UseQueryStateDefaultResult<
QueryDefinition<QueryArg, BaseQuery, string, ResultType, string>
>
> = TypedUseQueryStateResult<ResultType, QueryArg, BaseQuery, R> &
TypedUseQuerySubscriptionResult<ResultType, QueryArg, BaseQuery>

interface UseQuerySubscriptionOptions extends SubscriptionOptions {
/**
Expand Down Expand Up @@ -163,7 +183,23 @@ export type UseQuerySubscription<
> = (
arg: QueryArgFrom<D> | SkipToken,
options?: UseQuerySubscriptionOptions
) => Pick<QueryActionCreatorResult<D>, 'refetch'>
) => UseQuerySubscriptionResult<D>

export type UseQuerySubscriptionResult<
D extends QueryDefinition<any, any, any, any>
> = Pick<QueryActionCreatorResult<D>, 'refetch'>

/**
* Helper type to manually type the result
* of the `useQuerySubscription` hook in userland code.
*/
export type TypedUseQuerySubscriptionResult<
ResultType,
QueryArg,
BaseQuery extends BaseQueryFn
> = UseQuerySubscriptionResult<
QueryDefinition<QueryArg, BaseQuery, string, ResultType, string>
>

export type UseLazyQueryLastPromiseInfo<
D extends QueryDefinition<any, any, any, any>
Expand Down Expand Up @@ -339,6 +375,19 @@ export type UseQueryStateResult<
R
> = NoInfer<R>

/**
* Helper type to manually type the result
* of the `useQueryState` hook in userland code.
*/
export type TypedUseQueryStateResult<
ResultType,
QueryArg,
BaseQuery extends BaseQueryFn,
R = UseQueryStateDefaultResult<
QueryDefinition<QueryArg, BaseQuery, string, ResultType, string>
>
> = NoInfer<R>

type UseQueryStateBaseResult<D extends QueryDefinition<any, any, any, any>> =
QuerySubState<D> & {
/**
Expand Down Expand Up @@ -436,6 +485,22 @@ export type UseMutationStateResult<
reset: () => void
}

/**
* Helper type to manually type the result
* of the `useMutation` hook in userland code.
*/
export type TypedUseMutationResult<
ResultType,
QueryArg,
BaseQuery extends BaseQueryFn,
R = MutationResultSelectorResult<
MutationDefinition<QueryArg, BaseQuery, string, ResultType, string>
>
> = UseMutationStateResult<
MutationDefinition<QueryArg, BaseQuery, string, ResultType, string>,
R
>

/**
* 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.
*
Expand Down
6 changes: 6 additions & 0 deletions packages/toolkit/src/query/react/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,10 @@ const createApi = /* @__PURE__ */ buildCreateApi(
reactHooksModule()
)

export type {
TypedUseQueryHookResult,
TypedUseQueryStateResult,
TypedUseQuerySubscriptionResult,
TypedUseMutationResult,
} from './buildHooks'
export { createApi, reactHooksModule }
65 changes: 57 additions & 8 deletions packages/toolkit/src/query/tests/unionTypes.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import type { SerializedError } from '@reduxjs/toolkit'
import type { FetchBaseQueryError } from '@reduxjs/toolkit/query/react'
import type {
FetchBaseQueryError,
TypedUseQueryHookResult,
TypedUseQueryStateResult,
TypedUseQuerySubscriptionResult,
TypedUseMutationResult,
} from '@reduxjs/toolkit/query/react'
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { expectExactType, expectType } from './helpers'

const baseQuery = fetchBaseQuery()
const api = createApi({
baseQuery: fetchBaseQuery(),
baseQuery,
endpoints: (build) => ({
test: build.query<string, void>({ query: () => '' }),
mutation: build.mutation<string, void>({ query: () => '' }),
Expand Down Expand Up @@ -141,7 +148,6 @@ describe.skip('TS only tests', () => {
expectType<never>(result)
}
})
// pre41-remove-start
test('useQuery TS4.1 union', () => {
const result = api.useTestQuery()

Expand Down Expand Up @@ -207,7 +213,6 @@ describe.skip('TS only tests', () => {
expectType<never>(result)
}
})
// pre41-remove-end

test('useLazyQuery union', () => {
const [_trigger, result] = api.endpoints.test.useLazyQuery()
Expand Down Expand Up @@ -275,7 +280,6 @@ describe.skip('TS only tests', () => {
}
})

// pre41-remove-start
test('useLazyQuery TS4.1 union', () => {
const [_trigger, result] = api.useLazyTestQuery()

Expand Down Expand Up @@ -341,7 +345,6 @@ describe.skip('TS only tests', () => {
expectType<never>(result)
}
})
// pre41-remove-end

test('queryHookResult (without selector) union', () => {
const useQueryStateResult = api.endpoints.test.useQueryState()
Expand Down Expand Up @@ -501,7 +504,6 @@ describe.skip('TS only tests', () => {
})(result)
})

// pre41-remove-start
test('useMutation TS4.1 union', () => {
const [_trigger, result] = api.useMutationMutation()

Expand Down Expand Up @@ -552,5 +554,52 @@ describe.skip('TS only tests', () => {
expectType<never>(result)
}
})
// pre41-remove-end

test('"Typed" helper types', () => {
// useQuery
{
const result = api.endpoints.test.useQuery()
expectType<TypedUseQueryHookResult<string, void, typeof baseQuery>>(
result
)
}
// useQuery with selectFromResult
{
const result = api.endpoints.test.useQuery(undefined, {
selectFromResult: () => ({ x: true }),
})
expectType<
TypedUseQueryHookResult<string, void, typeof baseQuery, { x: boolean }>
>(result)
}
// useQueryState
{
const result = api.endpoints.test.useQueryState()
expectType<TypedUseQueryStateResult<string, void, typeof baseQuery>>(
result
)
}
// useQueryState with selectFromResult
{
const result = api.endpoints.test.useQueryState(undefined, {
selectFromResult: () => ({ x: true }),
})
expectType<
TypedUseQueryStateResult<string, void, typeof baseQuery, { x: boolean }>
>(result)
}
// useQuerySubscription
{
const result = api.endpoints.test.useQuerySubscription()
expectType<
TypedUseQuerySubscriptionResult<string, void, typeof baseQuery>
>(result)
}

// useMutation
{
const [trigger, result] = api.endpoints.mutation.useMutation()
expectType<TypedUseMutationResult<string, void, typeof baseQuery>>(result)
}
})
})

0 comments on commit 2a66246

Please sign in to comment.