1

I have the situation "A imports B and B imports A".

I've put the code of both files in one file and moved one of the file's relevant content into a variable declaration (not definition) almost at the beginning of the file.

I've practically solved the issue within JS view but the variable that contains the Redux store is not statically typed and so I get warnings from other files about this variable.

The variable is almost at the top of the file and it is not statically typed. I tried to make it statically typed by putting its definition inside a new function and tried to use let store: ReturnType<typeof buildMyStore> where buildMyStore is that function.

The buildMyStore variable declaration (a const arrow function) has the tooltip

buildMyStore implicitly has return type any because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. ts(7023)

The store variable declaration has the tooltip:

store is referenced directly or indirectly in its own type annotation. ts(2502)

If there is no answer to the question, please let me know which is the closest functional code change I can do.

Code

const buildMyStore = (api) => {
  return configureStore({
    reducer: rootReducer,
    middleware: getDefaultMiddleware =>
      getDefaultMiddleware({
        immutableCheck: false,
        serializableCheck: false,
        thunk: true
      })
        .concat([idleMiddleware as Middleware])
        .concat([api.middleware as Middleware]),
    devTools: process.env.NODE_ENV !== "production",
    enhancers: [(reduxBatch as unknown) as StoreEnhancer],
    preloadedState:
      typeof window !== "undefined"
        ? (window as any).__PRELOADED_STATE__
        : undefined
  })
};

let store: ReturnType<typeof buildMyStore>;

const axiosBaseQuery = (...): someComplexTypeHere => async ({ url, method, data }) => {
  // some code that uses store.getState().auth
};

const api = createApi(/* ...some code that uses axiosBaseQuery directly (not inside a callback)... */);

store = buildMyStore(api);

// exports here

Update 1

The exports include:

export const store;
export const { /* ... */ } = api;
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

Update 2

I think I temporarily and partially solved this by putting:

let store: any;

but this changes the TypeScript errors into some other errors: where a specific type was inferred for the parameters of a callback now I get an error saying that the inferred type is any. I wish to avoid this.

Update 3

The signature of axiosBaseQuery:

const axiosBaseQuery = (
  { baseUrl }: { baseUrl: string } = { baseUrl: "" },
): BaseQueryFn<
  {
    url: string;
    method?: AxiosRequestConfig["method"];
    data?: AxiosRequestConfig["data"];
    withAuth?: boolean;
  },
  unknown,
  unknown
> => async ({ url, method, data, withAuth }) => {
   // ...
};
silviubogan
  • 3,343
  • 3
  • 31
  • 57
  • 1
    Break the circular dependency. Normally you SHOULD NOT have anything about store in axiosBaseQuery but I see what you are trying to do. I still think it's HORRIBLY WRONG but ok if you want to organize your code that way. So the simplest way to avoid circular dependency is to remove axiosBaseQuery from api. Instead have buildMyStore pass axiosBaseQuery to api - if api is a function pass it as an argument. If api is an object implement an `.init()` or `.setBaseQuery()` function so that buildMyStore can pass axiosBaseQuery to it – slebetman Jul 16 '21 at 14:31
  • 1
    @slebetman `api` is an object AFAIK, created with [`createApi` function from RTKQ](https://redux-toolkit.js.org/rtk-query/api/createApi). What would be the alternative to calling `store.getState().auth` in axiosBaseQuery? – silviubogan Jul 17 '21 at 12:00
  • 1
    @slebetman I use `store.getState().auth` in `axiosBaseQuery` function to validate the username and auth token from the client on the server with basic HTTP auth for a REST API before many operations that require a user to be logged in. – silviubogan Jul 17 '21 at 12:12
  • 1
    The alternative is to pass `store` as an argument to `axiosBaseQuery`. That way you don't have any dependencies to store in axiosBaseQuery. You can either do it each time you make an http request or once in an init function to set the value of `store`. The NUMBER ONE lesson we've learned from OO programming is the lesson of dependency injection. – slebetman Jul 17 '21 at 13:50
  • 1
    I'm not sure if this helps but you can get the `RootState` from `rootReducer` rather than `buildMyStore`: `type RootState = ReturnType`. You can also get rid of an `as` assertion by typing the `api` argument. The minimum type that you need for this function is `(api: {middleware : Middleware})`. – Linda Paiste Jul 17 '21 at 16:09
  • 1
    If `auth` is the only part of the state that is used in `axiosBaseQuery` you can break the cycle by defining the state type for `axiosBaseQuery` as `{auth: ReturnType}`. – Linda Paiste Jul 17 '21 at 16:15
  • 1
    I'm confused by why `axiosBaseQuery` takes an argument. I started to play with this but I can't get very far without knowing all the types, ie. `someComplexTypeHere`. – Linda Paiste Jul 17 '21 at 16:33
  • @slebetman `const api = createApi(... axiosBaseQuery(store) ...);` <-- here `store` is not assigned a value yet so the behavior of axiosBaseQuery is wrong. – silviubogan Jul 19 '21 at 13:42
  • @LindaPaiste I've put the requested types in the *Update 3* section in the question. – silviubogan Jul 19 '21 at 15:19

0 Answers0