Josh M.
Josh M.

Reputation: 27773

Redux + Typescript + typesafe-actions:

Having an issue using Typescript 3.2.2 with typesafe-actions 3.0.0. I have created an example below showing the issue.

Basically in my reducer, payload is not defined on action because some of my actions are of type EmptyAction, which does not define a payload property. If I change these to have an empty payload ({}), then payload is defined for all actions, but now the type of payload is not being determined based on the type of the action (in the reducer).

I believe I've followed the Mighty Tutorial correctly.

Working example: https://github.com/JoshMcCullough/react-ts-test

action-types.ts

export default {
    GET_PROFILES_REQUEST: "GET_PROFILES_REQUEST",
    GET_PROFILES_SUCCESS: "GET_PROFILES_SUCCESS",
    GET_PROFILES_FAILURE: "GET_PROFILES_FAILURE",
};

actions.ts

import { action } from "typesafe-actions";
import { ApiErrorResponse } from "../../app/api/ApiErrorResponse";
import { Profile } from "../../app/models/Profile";
import ActionTypes from './action-types';

export const getProfilesRequest = () => action(ActionTypes.GET_PROFILES_REQUEST); // EmptyAction<string>
export const getProfilesSuccess = (profiles: Profile[]) => action(ActionTypes.GET_PROFILES_SUCCESS, { profiles }); // Action<string, { profiles: Profile[] }>
export const getProfilesFailure = (error: ApiErrorResponse) => action(ActionTypes.GET_PROFILES_FAILURE, { error }); // Action<string, { error: ApiErrorResponse }>

reducer.ts

import { ActionType, getType } from "typesafe-actions";
import * as Actions from './actions';
import { Profile } from "../../app/models/Profile";

// reducer
export type ProfileAction = ActionType<typeof Actions>;

export type State = {
    profiles: Profile[];
    errorMessage: string;
    loading: boolean;
};

const INITIAL_STATE = {
    profiles: [],
    errorMessage: null,
    loading: false,
};

export default (state: State = INITIAL_STATE, action: ProfileAction): State => {
    switch (action.type) {
        case getType(Actions.getProfilesRequest): return {
            ...state,
            loading: true,
        };

        case getType(Actions.getProfilesSuccess): return {
            ...state,
            profiles: action.payload.profiles,
            // ERROR: Property 'payload' does not exist on type 'EmptyAction<string> | PayloadAction<string, { profiles: Profile[]; }> | PayloadAction<string, { error: ApiErrorResponse; }>'.
            // Property 'payload' does not exist on type 'EmptyAction<string>'. [2339]
            loading: false,
        };

        case getType(Actions.getProfilesRequest): return {
            ...state,
            errorMessage: action.payload.error.message,
            // ERROR: Property 'payload' does not exist on type 'EmptyAction<string> | PayloadAction<string, { profiles: Profile[]; }> | PayloadAction<string, { error: ApiErrorResponse; }>'.
            // Property 'payload' does not exist on type 'EmptyAction<string>'. [2339]
            loading: false,
        };

        default: return state;
    }
};

Upvotes: 1

Views: 4877

Answers (1)

Josh M.
Josh M.

Reputation: 27773

Turns out my action types were not defined as const! I had overlooked it, since I'm upgrading an existing codebase.

The solution is to make sure they're consts:

action-types.ts

export const GET_PROFILES_REQUEST = "GET_PROFILES_REQUEST";
export const GET_PROFILES_SUCCESS = "GET_PROFILES_SUCCESS";
export const GET_PROFILES_FAILURE = "GET_PROFILES_FAILURE";

Upvotes: 1

Related Questions