3

I'm having hard time setting the types for this function:

interface fetchedCountries {
    mergedArray?: [];
}

export const fetchCountries = () => {
    return (dispatch:(() => void)) => {
        console.log(dispatch)
        fetch(countryListJsonFile)
            .then((response) => response.json())
            .then((jsonData: fetchedCountries) => {
                const array = Object.entries(jsonData)[0];
                const countries = array[1].map((el: any) => {
                    return el._id;
                }).sort();
                dispatch(setCountries(countries));
            })
            .catch(function (err) {
                console.error(err);
            });
    };
};

it says that setCountries expected 0 arguments, but got 1.

I've tried to follow different guides, such as this, but I couldn't make it work. I got lost. I want here to get rid of all the any types, and give the real ones.

The store has exported:

export type RootState = ReturnType<typeof store.getState>;
export type AppState = ReturnType<typeof rootReducer>;
export type AppDispatch = typeof store.dispatch;

Here is the slice:

import { createSlice } from '@reduxjs/toolkit';

const initialState = {
    countryList: [],
};

const slice = createSlice({
    name: 'countryList',
    initialState,
    reducers: {
        setCountries(state, action) {
            state.countryList = action.payload;
        },
    },
});

export const { setCountries } = slice.actions;
export default slice.reducer;

Can anyone please help?

4
  • 1
    How did you define setCountries? Commented Aug 27, 2021 at 10:50
  • 1
    setCountries is not stated in code. Commented Aug 27, 2021 at 10:54
  • As being stated in above comments, I would suspect that you haven't properly defined setCountries() action creator, I believe, it has to be something, like const setCountries = (country: string) => ({type: YOUR_CORRESPONDING_ACTION_TYPE_HERE, country}) Commented Aug 27, 2021 at 11:56
  • I just added the slice. Please check my original question Commented Aug 27, 2021 at 11:59

2 Answers 2

1

There are a few issues here in your code are all related. You need to properly define:

  1. The type of dispatch in your fetchCountries function.
  2. The type of the payload for setCountries.
  3. The type of your state.
  4. The type of your API response.

Incorrect or missing types higher up in the chain can cause issues further down. For example, when you find yourself setting a type in a .map() callback like this:

array[1].map((el: any) => {

It means that the array itself (array[1]) has the wrong type. So let's figure out where it's going wrong.


1. Dispatch Type

@T.D. Stoneheart is correct. The expected 0 arguments, but got 1 error comes from calling dispatch(...), not from calling setCountries(...).

Your definition dispatch:(() => void) says that dispatch does not take any arguments. This is obviously incorrect.

The good news is that you already have the correct type elsewhere in your code. It is the AppDispatch which you exported from your store file.

export const fetchCountries = () => {
    return (dispatch: AppDispatch) => {

This fix is enough to resolve all red-underlined errors. But there are some other mistakes and omissions that you may want to fix.


2. Payload Type

If you don't explicitly set the type for setCountries(state, action), then your payload type becomes any. This is fine, but not ideal. It says that "anything goes" which can make it hard to see genuine problems.

To assign the correct type to the action, import the PayloadAction utility type for redux toolkit:

import { createSlice, PayloadAction } from '@reduxjs/toolkit';

And use it with the type of your payload, which is an array of countries. Looking at your thunk, these seem to be string?:

reducers: {
    setCountries(state, action: PayloadAction<string[]>) {
        state.countryList = action.payload;      
    },
},

3. State Type

Remember how I said that any can hide genuine problems? If you followed step 2 then you should be seeing one of those now.

The assignment of state.countryList = action.payload; is giving an error:

Type 'string[]' is not assignable to type 'never[]'.

Your state.countryList has type never[] because it had an initial value of [] and that's all that TypeScript knows. It doesn't know that this is supposed to be an array of country ids. You can fix that by assigning a more accurate type to your `initialState.

Either do this:

const initialState = {
    countryList: [] as string[],
};

Or this:

interface SliceState { // can name it whatever
    countryList: string[];
}

const initialState: SliceState = {
    countryList: [],
};

Having a correct type here will make it much, much easier to use data that you've selected from your state because now RootState has the correct type for the countryList property.


4. API Response Type

interface fetchedCountries {
    mergedArray?: [];
}

This type is saying that the JSON from your response is an object which maybe has a property mergedArray which is an empty array. That's it.

I'm not sure what the actual data looks like, but perhaps something like this?

interface Country {
    _id: string;
}

interface FetchedCountries {
    mergedArray: Country[];
}

So now you don't need to use (el: any) because TypeScript already knows that el is a Country object.

.then((jsonData: FetchedCountries) => {
    const countries = jsonData.mergedArray
        .map(el => el._id)
        .sort();
    dispatch(setCountries(countries));
})

TypeScript Playground Link

Sign up to request clarification or add additional context in comments.

Comments

1

Actually, the problem lies in the parameter type of the returning function.

You returned (dispatch: ((/* no parameters */) => void)) => {} but you called dispatch(setCountries(countries)) which has one argument setCountries(countries). Setting correct number of parameters will fix, like return (dispatch: ((something: unknown) => void)) => {}.

2 Comments

return (dispatch: AppDispatch) => {
Thanks for the clarification; I haven't used Redux much, so I haven't known the correct type for it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.