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

Issue with Selector types on Code Split API #2484

Closed
littlejon opened this issue Jul 4, 2022 · 3 comments
Closed

Issue with Selector types on Code Split API #2484

littlejon opened this issue Jul 4, 2022 · 3 comments
Labels
bug Something isn't working typescript

Comments

@littlejon
Copy link

I have an application where I have split features into separate areas and I'm having trouble trying to use the endpoint select method.

I understand the code provided below is a little useless real world as there is nothing to select from (it is just a minimal reproduction showing the typing issue), what I am trying to do in my app is create memoized selectors for already fetched data.

Reproduction repos:
(code split not working): https://github.com/littlejon/rtk-selector-typing-issue/tree/code_splitting_not_working
(all in the createApi): https://github.com/littlejon/rtk-selector-typing-issue/tree/working_all_in_one

store.ts

import { configureStore } from "@reduxjs/toolkit";
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import { api } from "./services/api";

export const store = configureStore({
    reducer: {
        [api.reducerPath]: api.reducer
    },
    middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(api.middleware),
});

export type AppDispatch = typeof store.dispatch;
export type AppState = ReturnType<typeof store.getState>;
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<AppState> = useSelector;

api.ts

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

export interface AirQualityStation {
    station_id: string;
    station_name: string;
    region_id: string
    link: string;
    start_date: string;
    end_date: string;
    latitude: number;
    longitude: number;
}

export const api = createApi({
    reducerPath: "api",
    baseQuery: fetchBaseQuery({
      baseUrl: "https://airquality.des.qld.gov.au/v1",
    }),
    endpoints: () => ({})
});

export const airQualityApi = api.enhanceEndpoints({
    addTagTypes: ["airQualityStations"]
}).injectEndpoints({
    overrideExisting: true,
    endpoints: (builder) => ({
        getStations: builder.query<AirQualityStation[], void>({
            query: () => ({
                url: "/stations?pagesize=10&pagenumber=1",
                method: "GET",
            }),
            providesTags: ["airQualityStations"]
        })
    })
})

export const { useGetStationsQuery } = airQualityApi;

export const selectAirQualityStationResults = airQualityApi.endpoints.getStations.select();

export const selectAirQualityStations = createSelector(
    selectAirQualityStationResults,
    (results) => results.data
);

App.tsx

import { selectAirQualityStations } from "./services/api";
import { useAppSelector } from "./store";
import "./styles.css";

export default function App() {
  const airQualityStations = useAppSelector(selectAirQualityStations);

  return (
    <div className="App">
        RTK Typing of Selector
    </div>
  );
}

Line 6 gives the following type error.

src/App.tsx:6:45 - error TS2345: Argument of type '((state: { api: CombinedState<ReplaceTagTypes<{}, "airQualityStations"> & { getStations: QueryDefinition<void, BaseQueryFn<string | FetchArgs, unknown, FetchBaseQueryError, {}, FetchBaseQueryMeta>, "airQualityStations", AirQualityStation[], "api">; }, "airQualityStations", "api">; }) => AirQualityStation[] | undefin...' is not assignable to parameter of type '(state: { api: CombinedState<{}, never, "api">; }) => AirQualityStation[] | undefined'.
  Types of parameters 'state' and 'state' are incompatible.
    Type '{ api: CombinedState<{}, never, "api">; }' is not assignable to type '{ api: CombinedState<ReplaceTagTypes<{}, "airQualityStations"> & { getStations: QueryDefinition<void, BaseQueryFn<string | FetchArgs, unknown, FetchBaseQueryError, {}, FetchBaseQueryMeta>, "airQualityStations", AirQualityStation[], "api">; }, "airQualityStations", "api">; }'.
      The types of 'api.provided' are incompatible between these types.
        Property 'airQualityStations' is missing in type 'InvalidationState<never>' but required in type 'InvalidationState<"airQualityStations">'.

When the API is defined all within the createApi without using enhanceEndpoints or injectEndpoints the type works fine.

Please let me know if there is any other detail I can provide

@littlejon
Copy link
Author

CodeSandbox version
https://codesandbox.io/s/interesting-meadow-4ck9j9

@phryneas phryneas added bug Something isn't working typescript labels Jul 4, 2022
@phryneas
Copy link
Member

phryneas commented Jul 4, 2022

Yesh this is a bit unfortunate. It is all on a type level, so for now it should be working just fine, but give a type error - until we get a better idea to do this, adding a // @ts-expect-error is probably the best you can do.

Generally though, you should probably be using useQuery with selectFromResult if you ever want to access cache values from components. The selectors are just an escape hatch and it is perfectly fine to call useQuery from 20 components at once.

@littlejon
Copy link
Author

@phryneas - Thanks for the advice.

For the time being I have just moved all the enhanceEndpoints tags to the main createAPI definition.

Thank you for the work you and all the other contributors have put into the project!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working typescript
Projects
None yet
Development

No branches or pull requests

3 participants