0

Building this reducer with typescript and are trying to combine a lot of types together to archive something ala this typescript mockup:

type/interface DishOrderState {
  id: string
  withoutIngredients: string[]
  [key: string]: string
  initialState: {
     id: string
     withoutIngredients: string[]
     [key: string]: string
  }
}

a state could look like this

{
  id: '123'
  withoutIngredients: ['tomato'],
  ["BASE"]: 'rice',
  initialState: {
    id: '123'
    withoutIngredients: [],
    ["BASE"]: 'fries'
  }
}

My attempt to archive it looks like this so far:

export type DishActionType =
  | { type: 'REMOVE_INGREDIENT'; ingredient: string }
  | { type: 'ADD_INGREDIENT'; ingredient: string }
  | { type: 'PICK_OPTION'; option: string; choice: string }
  | { type: 'RESET' }
  | { type: 'never' }

interface DishOptions {
  [key: string]: string
}

// https://stackoverflow.com/a/45261035/618099
type DishOrder = DishOptions & {
  id: string
  withoutIngredients: string[]
}

type DishOrderState = DishOrder & {
  initialState: DishOrder
}

function dishOrderReducer(state :DishOrderState, action :DishActionType) :DishOrderState {
  switch (action.type) {
    case 'REMOVE_INGREDIENT':
      return {
        ...state,
        withoutIngredients: [...state.withoutIngredients.push(action.ingredient).sort()],
      }
    case 'ADD_INGREDIENT':
      return {
        ...state,
        withoutIngredients: [...state.withoutIngredients].filter(
          (ingredient) => ingredient != action.ingredient
        ),
      }
    case 'PICK_OPTION':
      return {
        ...state,
        [action.option]: action.choice
      }]
    case 'RESET':
      return {
        ...state.initialState,
        initialState: {...state.initialState}
      }
    default:
      throw new Error(`No action case for this action type: ${action.type}`)
  }
}

But vscode screams at me with

(property) withoutIngredients: string[]
Type '{ withoutIngredients: any[]; id: string; initialState: DishOrder; }' is not assignable to type 'DishOrderState'.
  Type '{ withoutIngredients: any[]; id: string; initialState: DishOrder; }' is not assignable to type 'DishOptions'.
    Property 'withoutIngredients' is incompatible with index signature.
      Type 'any[]' is not assignable to type 'string'.

showing the vscode error

Hovering over the DishOrderState shows this:

type DishOrderState = DishOptions & {
    id: string;
    withoutIngredients: string[];
} & {
    initialState: DishOrder;
}
Norfeldt
  • 8,272
  • 23
  • 96
  • 152

1 Answers1

1

The problem is you're mixing together specific fields, like id: string, with indexed fields like [key: string] : string.

An indexed field declaration says "there could be an infinite number of additional fields, but they'll all have this kind of key and this kind of value". Think of it as a wildcard type of description. That mostly ends up overriding any specific fields you might list.

Beyond that, a couple other observations:

Right now you're writing this reducer the hard way, by writing all the TS types and update logic by hand. You should be using our official Redux Toolkit package to write the reducer, which will drastically simplify the amount of code you have to write in terms of immutable updates and TS types for actions. Also see our official "Usage with TS" guide page.

Beyond that, the state shape doesn't make much sense. For one thing, why is there a field called initialState nested inside of the state value? That's very confusing. Typically there's a variable named initialState defined outside the reducer, standalone, and the reducer references that by itself - it's not nested inside the state value.

markerikson
  • 63,178
  • 10
  • 141
  • 157
  • thank you very much for your answer. I'm using React hook useReducer and not Redux – Norfeldt Apr 20 '21 at 13:24
  • Fun fact: you can still use Redux Toolkit to write reducer functions that are only meant for use with `useReducer`! A reducer is _just_ a function - doesn't matter how it was implemented inside. And, RTK's `createSlice` lets you write reducer functions with minimal boilerplate and correct TS types. – markerikson Apr 20 '21 at 14:35