Vinicius
Vinicius

Reputation: 1365

Typescript in redux reducer

Following redux guide to typescript I came up with the following code for my reducer. Typescript can't infer the right types on the switch case statement, though.

Reading this answer and its pointed typescript docs, I was able to make it work using string literals on the type variable on the interface. But I don't wanna use literals in there, since I am exporting my actions strings in a Actions variable, like in the code below.

What am I missing to be able to make it work like on the redux guide?

export const Action = {
  action1:'ACTION1',
  action2:'ACTION2'
}

interface actionType1 {
  type: typeof Action.action1, 
  stringPayload:string
}

interface actionType2 {
  type: typeof Action.action2,
  objectPayload:{
    str: string
  } 
}

type actionType = actionType1 | actionType2;

interface stateType {
  str:string,
}

const defaultState = {
  str:''
};


const reducer = ( state = defaultState, action:actionType) : stateType => {
  switch(action.type){
    case Action.action1:
      return {
        str:action.stringPayload //stringPayload appears as error
      }
        /*
        [ts]
        Property 'stringPayload' does not exist on type 'actionType'.
        Property 'stringPayload' does not exist on type 'actionType2'.
        */ 

    case Action.action2:
      return action.objectPayload //objectPayload appears as error
      /*
      [ts]
      Property 'objectPayload' does not exist on type 'actionType'.
      Property 'objectPayload' does not exist on type 'actionType1'
      */

    default:
      return state  
  }
}

Upvotes: 0

Views: 3701

Answers (2)

ford04
ford04

Reputation: 74500

It compiles with errors, because Action.action1, Action.action2 in reducer resolve to string. If you hover over your Action type, you can see how it's interpreted:

const Action: {
    action1: string;
    action2: string;
}

You could also test that by writing following code

type TestAction2Prop = typeof Action.action2 //  type TestAction2Prop = string

To solve that, you have to tell TypeScript explicitely that no literal types of Action should be widened (e.g. 'ACTION1' to string). You do that by const assertions:

export const Action = {
  action1:'ACTION1',
  action2:'ACTION2'
}  as const

Playground

Upvotes: 1

Valerii
Valerii

Reputation: 2317

Try this:

export class Action {
    static action1:'ACTION1';
    static action2:'ACTION2';
}

interface actionType1 {
    type: typeof Action.action1,
    stringPayload:string
}

interface actionType2 {
    type: typeof Action.action2,
    objectPayload:{
        str: string
    }
}

type actionType = actionType1 | actionType2;

interface stateType {
    str:string,
}

const defaultState = {
    str:''
};


const reducer = ( state = defaultState, action:actionType) : stateType => {
    switch(action.type){
        case Action.action1:
            return {
                str:action.stringPayload //stringPayload appears as error
            }
        /*
        [ts]
        Property 'stringPayload' does not exist on type 'actionType'.
        Property 'stringPayload' does not exist on type 'actionType2'.
        */

        case Action.action2:
            return action.objectPayload //objectPayload appears as error
        /*
        [ts]
        Property 'objectPayload' does not exist on type 'actionType'.
        Property 'objectPayload' does not exist on type 'actionType1'
        */

        default:
            return state
    }
}

Upvotes: 0

Related Questions