Search code examples
reactjstypescriptdependency-injectionredux-toolkitredux-thunk

Redux Toolkit typescript error when inyecting extra argument in Thunk Middleware


I'm trying to customize the Redux Toolkit`s included Middleware to add an extra argument. This extra argument is an implementation of a Repository.

When I configure the store I add that extra argument:

export const store = configureStore({
 reducer: {
  students: students,
 },
 middleware: (getDefaultMiddleware) =>
  getDefaultMiddleware({
   thunk: {
    extraArgument: createAPIStudentRepository,
  },
  serializableCheck: false,
 }),
});

createAPIStudentRepository is of type StudentRepository:

export const createAPIStudentRepository = (): StudentRepository => {
 return {
   loadStudents: async (query: string, page: number, limit: number) => {
     const response = await fetch(API_STUDENTS_URL(query, page, limit));
     const results = (await response.json()) as Student[];
     return results;
  },
 };
};

And here is the Student repository:

export interface StudentRepository {
 loadAStudents(
   query: string,
   page: number,
   limit: number
 ): Promise<Student[]>;
}

Then, in my Redux Thunk I want to use the createAPIStudentRepository that I injected when configuring the store:

interface StudentParams {
  query?: string;
  page?: number;
  limit?: number;
}

export const fetchStudents = createAsyncThunk(
  'student/fetch',
  async (
    params: StudentParams,
    { fulfillWithValue, rejectWithValue, extra }
  ) => {
    const { query = '', page = 1, limit = 10 } = params;

   try {
     //TODO: here is the problem, extra() throws an error: "extra" is of type 
     //"unknown"
     const studentRepository = extra();
     const results = await studentRepository.loadAStudents(
       query,
       page,
       limit
     );

     return { results, page, query };
   } catch (error: unknown) {
     console.log(error);
     return rejectWithValue("Error: couldn't fetch Students");
   }
 }
);

The issue is in the TODO line. This code works but I get a Typescript error: "extra" is of type "unknwon".

Any ideas of how I can let Typescript know the type?

Reference:


Solution

  • You can explicitly type the createAsyncThunk function. See Usage with Typescript: createAsyncThunk for details.

    interface StudentParams {
      query?: string;
      page?: number;
      limit?: number;
    }
    
    export const fetchStudents = createAsyncThunk<
      // Return type
      { results: Student[], page: number, query: string },
      // Argument
      StudentParams,
      // ThunkApi
      {
        extra: () => StudentRepository
      }
    >(
      'student/fetch',
      async (
        params: StudentParams,
        { fulfillWithValue, rejectWithValue, extra }
      ) => {
        const { query = '', page = 1, limit = 10 } = params;
    
        try {
          const studentRepository = extra();
          const results = await studentRepository.loadAStudents(
            query,
            page,
            limit
          );
    
          return { results, page, query };
        } catch (error: unknown) {
          console.log(error);
          return rejectWithValue("Error: couldn't fetch Students");
        }
      }
    );
    

    Or you can cast the extra type. I believe the following should work.

    interface StudentParams {
      query?: string;
      page?: number;
      limit?: number;
    }
    
    export const fetchStudents = createAsyncThunk(
      'student/fetch',
      async (
        params: StudentParams,
        { fulfillWithValue, rejectWithValue, extra }
      ) => {
        const { query = '', page = 1, limit = 10 } = params;
    
        try {
          const studentRepository = (extra as () => StudentRepository)();
          const results = await studentRepository.loadAStudents(
            query,
            page,
            limit
          );
    
          return { results, page, query };
        } catch (error: unknown) {
          console.log(error);
          return rejectWithValue("Error: couldn't fetch Students");
        }
      }
    );