Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RTK Query: circular type reference #2191

Closed
jeron-diovis opened this issue Apr 1, 2022 · 10 comments
Closed

RTK Query: circular type reference #2191

jeron-diovis opened this issue Apr 1, 2022 · 10 comments

Comments

@jeron-diovis
Copy link

jeron-diovis commented Apr 1, 2022

I saw #1126 and #1518, they are both about async thunks, and probably can be solved with workaround about how reducer is exported (didn't try it myself).

I need AppState in RTQ endpoint definition, like this:

export const MyApi = createApi({
  baseQuery: ...,

  endpoints: builder => ({
    whatever: builder.query<..., void>({
      queryFn: (_, { getState }, opts, baseQuery) => {
        const state = getState() as AppState
        return baseQuery(`users/${state.user.id}/whatever`)
      },
    }),
  }),
})

I don't even have initial state to try this approach.
What can I do in this case, aside of casting state into any?

Obviously, in this simple example I can rethink my api and just pass user id as explicit parameter. But in general case, is it possible to solve?

@markerikson
Copy link
Collaborator

I'm not quite sure what the question is here. Are you seeing an actual error right now, or looking ahead and wondering if this might cause a problem? What have you tried so far?

@jeron-diovis
Copy link
Author

This is an actual error.
I had plans to build api like this. I assume variety of endpoints like one above, so I didn't want to pass those parameters explicitly everywhere.
I tried this very first step, as shown above, and got the circular reference problem.

@markerikson
Copy link
Collaborator

Can you put together a CodeSandbox that shows this error happening?

@thorn0
Copy link
Contributor

thorn0 commented Apr 2, 2022

see #1685 (comment)
https://codesandbox.io/s/elated-golick-b7kv2n?file=/src/index.tsx
Type alias 'AppState' circularly references itself.ts(2456)

@phryneas
Copy link
Member

phryneas commented Apr 3, 2022

That will probably go away once you type your mutation explicitly so TS knows that there is no need to take the RootState into account for infering the result type.

saveRecords: mutation<Result, Argument>({

@thorn0
Copy link
Contributor

thorn0 commented Apr 4, 2022

That doesn't help. The issue arises whenever the result of a baseQuery call is returned from queryFn.

@phryneas
Copy link
Member

phryneas commented Apr 4, 2022

Yeah, seems like it needs one or two more explicit type annotation to break the circle. It's a bit weird since it should really not need that, but sometimes TS is just TS unfortunately.

const api = createApi({
  reducerPath: "api",
  baseQuery: fetchBaseQuery({ baseUrl: "/" }),
  endpoints: ({ query, mutation }) => ({
    saveRecords: mutation<SaveResult, undefined>({
      queryFn: async (
        args,
        queryApi,
        extra,
        baseQuery
      ): Promise<
        QueryReturnValue<SaveResult, FetchBaseQueryError, FetchBaseQueryMeta>
      > => {
        const getState = queryApi.getState as () => AppState;
        const result = await baseQuery("/save/" + getState().foo.bar);
        return result as QueryReturnValue<
          SaveResult,
          FetchBaseQueryError,
          FetchBaseQueryMeta
        >;
      }
    })
  })
});

An alternative would be to cast not to AppState, but to something like { sliceINeed: SliceType }

@thorn0
Copy link
Contributor

thorn0 commented Apr 8, 2022

Here's a workaround that somehow worked for me. I created a QueryFn type:

interface AppQueryApi extends BaseQueryApi {
  getState(): AppState; // for whatever reason this doesn't trigger the circular reference error
}

type FetchBaseQuery = <T>(
  arg: string | FetchArgs
) => MaybePromise<QueryReturnValue<T, FetchBaseQueryError, FetchBaseQueryMeta>>;

type QueryFn<out Result, in Arg> = (
  arg: Arg,
  queryApi: AppQueryApi,
  extra: any,
  baseQuery: FetchBaseQuery
) => MaybePromise<QueryReturnValue<Result, FetchBaseQueryError, FetchBaseQueryMeta>>;

const saveRecordsQueryFn: QueryFn<SaveResult, undefined> =
  async (args, { getState }, extra, baseQuery) => {
    const result = await baseQuery<SaveResult>("/save/" + getState().foo.bar);
    return result;
  };

const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: "/" }),
  endpoints: ({ query, mutation }) => ({
    saveRecords: mutation({ queryFn: saveRecordsQueryFn })
  })
});

As a nice bonus, this approach allows me to extract complex queryFn functions to separate files. While I'm relatively happy with it, it seems fragile, not really proof against future versions of TypeScript.

CodeSandbox

@thorn0
Copy link
Contributor

thorn0 commented Apr 27, 2022

So here's an update on the approach from my previous message after trying to use it for a while.

TL;RD: kind of works, but not really usable

While tsc always checks the project successfully from the command line, the results are unstable in the editor. The same project can be shown with or without errors depending apparently on the order in which the language service checks the files. For example, open the code sandbox from the previous message. Most probably, you'll see errors. But if you edit anything in index.tsx, the errors will disappear. That's quite annoying.

Currently, in my IDE (VS Code), when the file with the QueryFn type definition is open, there is no errors shown. Once I close it, circular reference errors start appearing. I wonder if it's possible somehow to make TS start the inference from that file and then always "remember" about it.

@markerikson
Copy link
Collaborator

I'm going to close this because I don't think there's anything immediately actionable for us to fix here.

@markerikson markerikson closed this as not planned Won't fix, can't repro, duplicate, stale Jun 29, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants