2

I am using Typescript in my React app where I am also using Redux Toolkit for state management. The data is coming from Express Api and everything works perfectly if I implement Redux without Typescript but if I use Typescript I get this error:

ERROR in src/features/product/productSlice.tsx:61:27
TS2571: Object is of type 'unknown'.
     59 |         return data;
     60 |     } catch ( error ) {
   > 61 |         const message = ( error.response && error.response.data && error.response.data.message ) || error.message || error.toString();
       |                           ^^^^^
     62 |         return thunkAPI.rejectWithValue( message );
     63 |     }
     64 | } );

Now I am really new to Typescript as well as Redux Toolkit and I don't know how it fix it.

I am just making a get request in thunk function to fetch data at page load.

This is how productsSlice.tsx file looks like:

import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
import { RootState, AppThunk } from '../../app/store';
import getAllProducts from './productService';

interface Specifications {
    display: string,
    processor: string,
    frontCam: string,
    rearCam: string,
    ram: string,
    storage: string,
    batteryCapacity: string,
    os: string;
}

interface Products {
    title: string,
    slug: string,
    description: string,
    color: string,
    price: number,
    image: string,
    specifications: Specifications;
};

export interface Data {
    success: boolean;
    message: string;
    data: Products[] | null;
}

interface ProductState {
    products: Products[] | null,
    isError: boolean;
    isSuccess: boolean;
    isLoading: boolean;
    message: string;
}

const initialState: ProductState = {
    products: null,
    isError: false,
    isSuccess: false,
    isLoading: false,
    message: ''
};

export const getProducts = createAsyncThunk( 'products/getAllProducts', async ( _, thunkAPI ) => {
    try {
        const data = await getAllProducts();
        return data;
    } catch ( error ) {
        const message = ( error.response && error.response.data && error.response.data.message ) || error.message || error.toString();
        return thunkAPI.rejectWithValue( message );
    }
} );

export const productSlice = createSlice( {
    name: 'products',
    initialState,
    reducers: {},
    extraReducers: ( builder ) => {
        builder
            .addCase( getProducts.pending, ( state ) => {
                state.isLoading = true;
            } )
            .addCase( getProducts.fulfilled, ( state, action ) => {
                state.isLoading = false;
                state.isSuccess = true;
                state.products = action.payload.data;
            } )
            .addCase( getProducts.rejected, ( state, action ) => {
                state.isLoading = false;
                state.isError = true;
                state.message = action.payload;
                state.products = null;
            } );
    }
} );


export const getProductsSelector = ( state: RootState ) => state.products;
export default productSlice.reducer;

This is productService.tsx:

import axios from 'axios';
import { Data } from './productSlice';

const API_URL: string = '/api/phones';

const getAllProducts = async (): Promise<Data> => {
    const response = await axios.get( API_URL );
    return response.data;
};

export default getAllProducts;

And this is the store:

import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit';
import products from '../features/product/productSlice';

export const store = configureStore( {
    reducer: {
        products
    },
} );

export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;
export type AppThunk<ReturnType = void> = ThunkAction<
    ReturnType,
    RootState,
    unknown,
    Action<string>
>;

1 Answer 1

3

In TypeScript, everything in a catch block is unknown - after all, upstream code could throw 5, throw "foo", throw window, throw undefined, or throw new Error("message") - so you have no guarantee that there is error.response.

If you know that you are expecting a class error and know the class, you can just do

    } catch ( error ) {
       if (error instanceof AxiosError) {
        const message = ( error.response && error.response.data && error.response.data.message ) || error.message || error.toString();
        return thunkAPI.rejectWithValue( message );
        }
       // unhandled non-AxiosError goes here
       throw error
    }
2
  • Okay I got that part to work with this if ( axios.isAxiosError( error ) ) { const message = ( error.response && error.response?.data && error.response?.data.message ) || error.message || error.toString(); return thunkAPI.rejectWithValue( message ); } else { throw new Error( 'different error than axios' ); } but now state.message in extraReducers is saying Type 'unknown' is not assignable to type 'string'. Any idea why?
    – s.khan
    Commented Mar 9, 2022 at 12:57
  • You need to type the rejectValue generic value of your createAsyncThunk call. See the TypeScript docs on createAsyncThunk
    – phry
    Commented Mar 9, 2022 at 15:08

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.