3

I'm new to typescript at the moment and I'm trying to combine it to redux actions and reducers. I'm getting an error right now and I want the best approach to refactoring the code. This was my method, and I need assistance.

Action

import Axios from "axios";

export const fetchTODO = term => async dispatch => {
  dispatch({ type: "FETCH_TODO" });

  try {
    const response = await Axios.get(`/api/TODO/${term}`, {});
    dispatch({
      type: "FETCH_TODO_SUCCESS",
      payload: response.data
    });
  } catch (error) {
    dispatch({ type: "FETCH_TODO_FAILURE", error });
  }
};

Reducer

const initialState = {
  payload: [],
  isLoading: false,
  error: {}
};

export default (state = initialState, { type, payload, error }) => {
  switch (type) {
    case "FETCH_TODO":
      return { ...state, isLoading: true };

    case "FETCH_TODO_SUCCESS":
      return { ...state, payload, isLoading: false };

    case "FETCH_TODO_FAILURE":
      return { ...state, error: error, isLoading: false };

    default:
      return state;
  }
};

Typescript code

types.tc

export enum ActionTypes {
  fetchTodos,
  fetchTodosSuccess,
  fetchTodosFailure
}

Action

import { ActionTypes } from "./types";
import Axios from "axios";
import { Dispatch } from "redux";

export interface Todo {
  id: number;
  title: string;
  completed: boolean;
}

export interface FetchTodoAction {
  type: ActionTypes;
  payload?: Todo[];
  error?: object;
}

export const fetchTransaction = (term: string) => async (
  dispatch: Dispatch
) => {
  dispatch({ type: ActionTypes.fetchTodos });

  try {
    const response = await Axios.get<Todo[]>(
      `https://jsonplaceholder.typicode.com/todos/`
    );
    dispatch<FetchTodoAction>({
      type: ActionTypes.fetchTodosSuccess,
      payload: response.data
    });
  } catch (error) {
    dispatch({ type: ActionTypes.fetchTodosFailure, error });
  }
};

StateInterface object

export interface StateInterface {
  payload?: Todo[];
  isLoading: boolean;
  error?: object;
}

Reducer


import { Todo, FetchTodoAction } from "./../actions/index";
import { ActionTypes } from "../actions/types";
import { StateInterface } from ".";

const initialState = {
  payload: [],
  isLoading: false,
  error: {}
};

export const todosReducer = (
  state: StateInterface = initialState,
  { type, payload, error }: FetchTodoAction
) => {
  switch (type) {
    case ActionTypes.fetchTodos:
      return { ...state, isLoading: true };

    case ActionTypes.fetchTodosSuccess:
      return { ...state, payload, isLoading: false };

    case ActionTypes.fetchTodosFailure:
      return { ...state, error: error, isLoading: false };

    default:
      return state;
  }
};

I got this error after the code and anyone can please tell me the best implementation

(alias) const todosReducer: (state: StateInterface | undefined, { type, payload, error }: FetchTodoAction) => StateInterface
import todosReducer
No overload matches this call.
  Overload 1 of 3, '(reducers: ReducersMapObject<StateInterface, any>): Reducer<CombinedState<StateInterface>, AnyAction>', gave the following error.
    Argument of type '{ todos: (state: StateInterface | undefined, { type, payload, error }: FetchTodoAction) => StateInterface; }' is not assignable to parameter of type 'ReducersMapObject<StateInterface, any>'.
      Object literal may only specify known properties, and 'todos' does not exist in type 'ReducersMapObject<StateInterface, any>'.

please, I'm really looking forward to some support, and thank you in advance

1 Answers1

2

This error is thrown because TypeScript prevents you from having an argument to be option (i.e. undefined) when following arguments are required (i.e. not undefined).

You can fix this by inferring the type from the initialState.

Eg. const initialState: StateInterface { ... }

Now you no longer have to set the type for state in your reducer arguments, since TypeScript will know that this value will always be defined.

Eg. export const todosReducer = (state = initialState, ...) => { ... }

Therefore, change your reducer code to the following:

import { Todo, FetchTodoAction } from "./../actions/index";
import { ActionTypes } from "../actions/types";
import { StateInterface } from ".";

// Define initial state type here
const initialState: StateInterface = {
  payload: [],
  isLoading: false,
  error: {}
};

export const todosReducer = (
  state = initialState, // state type is now inferred from initialState
  { type, payload, error }: FetchTodoAction
) => {
  switch (type) {
    case ActionTypes.fetchTodos:
      return { ...state, isLoading: true };

    case ActionTypes.fetchTodosSuccess:
      return { ...state, payload, isLoading: false };

    case ActionTypes.fetchTodosFailure:
      return { ...state, error, isLoading: false };

    default:
      return state;
  }
};

EDIT: If this still doesn't work then changing { type, payload, error }: FetchTodoAction to { type, payload, error }: FetchTodoAction | undefined should do the trick.

Barry Michael Doyle
  • 9,333
  • 30
  • 83
  • 143