I'm Joe Too
I'm Joe Too

Reputation: 5840

Typescript Redux Action Generic with Iterable Type

Solved: If you run into this exact issue, don't make my bonehead mistake. I was trying to deconstruct an object in Object.assign when I should have just been passing the object. @Eldar and @Akxe were kind enough to put fresh eyes on the code and help me see my mistake in the comments.


Problem: I'm trying to create a reusable Entity for my redux actions in typescript so I can just pass a type in the reducer, but it's complaining that my type is not iterable and I'm not sure how to solve this. I've blindly tried adding typeof and Iterable to no avail, so I'm looking for a little help and explanation.

I have an Entity for my user as:

export default interface UserEntity {
  uid: string | null;
  firstName: string | null;
}

and created a generic Entity for my action as:

export default interface StoreAction<T> {
  payload: T;
  type: string;
}

In my reducer, I hoped to implement it like this:

const initialState: UserEntity = {
  uid: null,
  firstName: null,
};
export default (state = initialState, action: StoreAction<UserEntity>) => {
  switch (action.type) {
    case 'Some string':
      return Object.assign({}, state, ...action.payload); // Warning that action.payload is not iterable
    default:
      return state;
  }
};

but VS Code is complaining Type 'UserEntity' must have a '[Symbol.iterator]()' method that returns an iterator. While I understand what it's complaining about, I'm at a loss on how to solve it.

If it's not obvious here, I want to pass my action a payload of json in the structure of this UserEntity and then be able to use the spread operator to merge it with my redux state. And I was hoping to make this a generic because some actions will have arrays, some might just be a boolean, etc. The redux site gives similar examples showing spread operators with interfaces as if this should work, but I'm clearly missing something in mine.

Edit: Adding my tsconfig.json:

{
  "compilerOptions": {
    "target": "esnext",
    "module": "commonjs",
    "lib": [
      "es6"
    ],
    "allowJs": true,
    "jsx": "react-native",
    "noEmit": true,
    "isolatedModules": true,
    "strict": true,
    "moduleResolution": "node",
    "baseUrl": "./",
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true
  },
  "exclude": [
    "node_modules",
    "babel.config.js",
    "metro.config.js",
    "jest.config.js"
  ]
}

Upvotes: 0

Views: 431

Answers (1)

Akxe
Akxe

Reputation: 11515

Object.assign({}, state, ...action.payload) is actually an invalid syntax. You have two options on how to fix it.

Either pass the object to the Object.assign as-is: return Object.assign({}, state, action.payload)

, or use the deconstruction, as it does the same thing but might offer better performance (for some engines) return { ...state, ...action.payload }.

Upvotes: 1

Related Questions