redux-toolkit
redux-toolkit copied to clipboard
Review and update RTKQ TS type visibility
We kinda hand-waved reviewing some of the TS types. We should make sure that we've got exactly the stuff we want marked as public.
Not perfectly sure this is the right place for a comment on this, but I've run into trouble with this. I'd like to be able to pass the result of a query hook into a view component, but to do so cleanly would need this to work, I think:
import { UseQueryResult } from "@reduxjs/toolkit/dist/query"; // "has no exported member..."
For now, I can just copy-paste the type definition from here:

but it seems we'd want to be able to import that directly.
This is just a simplified type, correct? Normally, data is not optional if isSuccess is true. But something like this really is needed if I want to move the call of a query hook into a custom hook that returns the result directly. Otherwise, I cannot specify the return type of that custom hook.
function useCustomHook(): ??? {
const result = useSomeQuery()
return result
}
I tried using ReturnType<typeof useSomeQuery> but that doesn't work either because this type's property isSuccess is of type any. This means it cannot be used in a conditional.
@alinnert are you looking for these types? https://github.com/reduxjs/redux-toolkit/pull/2580/files
@phryneas technically I'm looking for UseQueryHookResult, yes. But I cannot use it for two reasons: When I hover over the result variable in VSCode it doesn't show me the full type because it's too long. A part of it is truncated. That means I don't know how to fully use that type. The second reason is the result type includes UseQueryStateDefaultResult<...> which isn't exported and therefore I cannot access.
@alinnert What do you mean "cannot access"?
Usage should be UseQueryHookResult<YourDataTypeHere>.
If it is truncated, hold down Ctrl and click on it and it will give you the full definition.
What do you mean "cannot access"?
Nevermind. I meant I cannot import (= access) UseQueryStateDefaultResult because it's not exported. But it shouldn't be necessary anyway.
Okay, now I know that YourDataTypeHere needs to be of type QueryDefinition<...>. But I still don't know how to get that type. I know that postQuoteDocumentMetadataById (result of builder.query()) in the code below is such a QueryDefinition. Do I need this one? But I don't know how I get that type from here into my custom hook.
The closest I get is UseQueryHookResult<typeof externalSFApi['endpoints']['postQuoteDocumentMetadataById']>. But that's only ApiEndpointQuery<QueryDefinition<...>>. That's close but it still doesn't work. What am I missing?
import { createApi } from '@reduxjs/toolkit/query/react'
import { ApiDocumentMetadata } from '../../services/request/apiTypes/documentMetadata'
import { baseQuery } from './baseQuery'
export const externalSFApi = createApi({
reducerPath: 'externalSFApi',
baseQuery,
endpoints: (builder) => ({
postQuoteDocumentMetadataById: builder.query<
ApiDocumentMetadata,
{ sfId: string; productId: string }
>({
query: ({ sfId, productId }) => ({
method: 'post',
url: `quote/document/metadata/${sfId}/${productId}`,
}),
}),
}),
})
export const { usePostQuoteDocumentMetadataByIdQuery } = externalSFApi
No, you're making this a lot more complicated than it needs to be :)
It's whatever data type your hook returns. Like, for a getPokemon endpoint / hook, it'd be UseQueryHookResult<Pokemon>. So in this case, UseQueryHookResult<ApiDocumentMetadata>.
All that said, is there a reason you can't let TS infer the return type of your custom hook?
I tried this at first, but UseQueryHookResult<ApiDocumentMetadata> gives me the error "Type 'ApiDocumentMetadata' does not satisfy the constraint 'QueryDefinition<any, any, any, any, string>'.". That's how I ended up looking for QueryDefinition.
You mean skipping the return type definition? Basically, because of how we setup ESLint. It would result in an error on that end. Generally, I think explicit return types are a good idea. But I've noticed that typing custom hooks can get really difficult.
It's TypedUseQueryStateResult<ResultType, QueryArgument, BaseQuery> and you probably can just do TypedUseQueryStateResult<YourResult, unknown, any>
Whoops, my bad! That's what I get for trying to answer this without looking at the code :)
Okay, that works. Thank you very much! But could this potentially be made a little more straightforward? I'd need to look into this in more detail but maybe something like QueryHookResult<typeof apiInstance, 'nameOfTheEndpoint'>? Because apiInstance already has all the information that TypedUseQueryStateResult needs.
Sorry, but no. This is already a quite niche use case and I don't think that adding a lot more helpers here will bring substantial benefit - but it will add a maintenance burden.