redux-toolkit icon indicating copy to clipboard operation
redux-toolkit copied to clipboard

"Types of property 'isUninitialized' are incompatible." when extending a useQuery hook

Open denchen opened this issue 3 years ago • 2 comments

I'm trying to extend a useQuery() hook with the newly released TypedUseQueryHookResult type introduced in 1.8.4, but I'm getting the following:

Types of property 'isUninitialized' are incompatible.
  Type 'true' is not assignable to type 'false'.ts(2322)

The code (with CodeSandbox):

import {
  createApi,
  fetchBaseQuery,
  TypedUseQueryHookResult
} from "@reduxjs/toolkit/query/react";

type MyApiProps = {
  id: string;
};

type MyApiResponse = Array<{
  id: string;
  name: string;
}>;

export const api = createApi({
  reducerPath: "myApi",
  baseQuery: fetchBaseQuery({ baseUrl: "https://some-api.com/api/v1" }),
  tagTypes: ["MyApi"],
  endpoints: (build) => ({
    getData: build.query<MyApiResponse, MyApiProps>({
      query: ({ id }) => `my-api/${id}`
    })
  })
});

export const useGetData = (
  args: MyApiProps
): TypedUseQueryHookResult<
  MyApiResponse,
  MyApiProps,
  ReturnType<typeof fetchBaseQuery>
> =>
  api.useGetDataQuery(args, {
    selectFromResult: (results) => ({
      ...results,
      data: results.data?.filter((d) => d.name === "Harry")
    })
  });

Note that this usage of selectFromResult() works just fine if I use the auto-generated hook directly in a component.

denchen avatar Aug 31 '22 03:08 denchen

Weird enough, if you declare the return type like

export const useGetData = (
  args: MyApiProps
): Omit<
  TypedUseQueryHookResult<
    MyApiResponse,
    MyApiProps,
    ReturnType<typeof fetchBaseQuery>
  >,
  ""
>

so essentially omitting a property that does not exist - everything works :thinking:

phryneas avatar Sep 02 '22 14:09 phryneas

Most likely Omit works because it "downgrades" the union to an object type as Omit is not distributive.

I took a quick look with the debugger there, and I think I got good information to help you solve this. If we Compute every member of those unions etc then we can reason about this way easier: TS playground. You can see that the reported error is essentially the same here.

We can also sort all keys everywhere to make comparisons easier, but that changes the reported error (which is fine, it's just order-dependant): TS playground

We can also remove all but first types in the ExprType union because as far I've seen in the debugger it's already the first member of that union that doesn't match: TS playground

Then we can drop the fourth element of the RetType because it clearly can't be matched based on the currentType: TS playground

Then we can drop 2 first members of that RetType union too, because data property doesn't match: TS playground

And now we can conclude that those are not available at least because the error property is required in one of those union members of the TypedUseQueryHookResult (even though it's value is undefined) but it's optional in the UseQueryHookResult. This is defined on the third union member of the TypedUseQueryHookResult but the order in the union is not guaranteed so perhaps we shouldn't rely on this - it's likely that this matches the definition order though.

I think I've chosen a slight detour here, in this reasoning, because the easiest would be to just name all of the union members, like here. And then to compare them one by one with the ExprType - this allows us quickly to check if the reported error matches what we were seeing in the app~ or not. Based on that we can see that isUninitialized: true; is only in one of the union members of the TypedUseQueryHookResult so it's the only potential candidate from this PoV BUT at the same time its data property doesn't match the property type in the slimmed down UseQueryHookResult (we can't assign something to what expect undefined).

TLDR: there are a couple of assignability problems here.

Andarist avatar Sep 05 '22 08:09 Andarist