0

I want to set Typescript types for Create Reducer and I want to understand How can I use Generics in this hard construction.

Here I have Actions

State

Object where Keys is name of action type and value is function with 3 parameter (payload, state, type)

I want to make Auto types based on Passed Types to createReducer function

I have next example

enum ActionTypes {
  INCREMENT = 'INCREMENT',
  DECREMENT = 'DECREMENT',
  SET_COUNT = 'SET_COUNT',
}

type IncrementAction = IAction<ActionTypes.INCREMENT>;
type DecrementAction = IAction<ActionTypes.DECREMENT, 'hello' | 'world'>;
type SetCountAction = IAction<ActionTypes.SET_COUNT, number>;

type CounterState = {
  count: number;
  message?: string;
};

const state: CounterState = {
  count: 0,
};

type Action = IncrementAction | DecrementAction | SetCountAction;

// I want auto type inference for the action type
const counterReducer = createReducer<CounterState, Action>(state, {
  // [ActionTypes.INCREMENT]: (payload: unknown, state: CounterState, type: ActionTypes.INCREMENT)
  [ActionTypes.INCREMENT]: (payload, state, type) => ({ count: state.count + 1 }),

  // [ActionTypes.DECREMENT]: (payload: 'hello' | 'world', state: CounterState, type: ActionTypes.DECREMENT)
  [ActionTypes.DECREMENT]: (payload, state, type) => ({ count: state.count - 1 }),
  
  // [ActionTypes.SET_COUNT]: (payload: number, state: CounterState, type: ActionTypes.SET_COUNT)
  [ActionTypes.SET_COUNT]: (payload, state, type) => ({ count: payload }),
  ['*']: (payload: unknown, state: CounterState, type: string) => ({}),

  // here should be Error becase is not member of ActionTypes
  ['something']: (payload: unknown, state: CounterState, type: string) => ({}),
});

So, I have some code for it, but I cant resolve all types

export type IState = Record<string, any>;

export type IAction<T extends string = string, P = unknown> = {
  type: T;
  payload?: P;
};

export type Reducer<S, A extends IAction> = (
  payload: A['payload'],
  state: S,
  type: A['type'],
) => IState;

interface IHandlers<S, A extends IAction, T extends IAction['type'] = string> {
  [key: T]: Reducer<S, A>;
  ['*']: Reducer<S, A>;
}

export function createReducer<S extends IState, A extends IAction>(
  initialState: S = {} as S,
  handlers: IHandlers<S, A>,
) {
  return function reducer(state = initialState, action: A) {
    let newState = state;
    if (handlers.hasOwnProperty(action.type)) {
      const result = handlers[action.type](action.payload, state, action.type);
      newState = { ...state, ...result };
    }
    if (handlers['*']) {
      return { ...newState, ...handlers['*'](action.payload, newState, action.type) };
    }
    return newState;
  };
}

export default createReducer;
  • 2
    There are multiple errors in that code, and it's not clear which, if any of them, you are asking about. Please [edit] the code to be a [mre] that demonstrates the problem you are having. – jcalz Dec 05 '22 at 20:30

0 Answers0