TypeScript errors when I move switch statement cases to own functions?

Viewed 40

Im using TypeScript with Redux. This code works fine:

export type action =
  | {
      type: "do-other-thing";
      name: string;
    }
  | {
      type: "do-something";
      index: number;
    };

function reducer(state: state = initState, action: action): state {
  switch (action.type) {
    case "do-something":
        if (action.index === 0) return state;
        // More logic
    case "do-other-thing":
        // More logic
    default:
        return state;
  }

However I want to move the return cases from the switch statement to be their own functions:

function doSomething(state: state, action : action): state {
    if (action.index === 0) return state;
    // more logic
}

function reducer(state: state = initState, action: action): state {
  switch (action.type) {
    case "do-something":
        return doSomething(state, action);
    case "do-other-thing":
        // More logic
    default:
        return state;
  }

Now the doSomething function has a TypeScript error:

Property 'index' does not exist on type 'action'. Property 'index' does not exist on type '{ type: "do-something"; width: number; isLandscape: boolean; }'.ts(2339)

I can see that TypeScript isn't aware that due to the switch statement the action object will have and index property. Is there a way to make it aware of this?

2 Answers

Because you defined the parameter as action on the doSomething function, this allows anyone to pass any action in other places. You most likely want to actually define it to be a more restricted type like this:

type DoOtherThingAction = { type: "do-other-thing"; name: string; };
type DoSomeThingAction = { type: "do-something"; index: number; };
export type action = 
  | DoOtherThingAction
  | DoSomeThingAction;

and then:

function doSomething(state: state, action : DoSomeThingAction): state {
    if (action.index === 0) return state;
    // more logic
}

Function doSomething doesnt work with all actions but with action with index property, I would split some typying in order to be able to more clear type the demand:

type A = {
      type: "do-other-thing";
      name: string;
    };
type B = {
      type: "do-something";
      index: number;
    };
type Action = A | B

function doSomething(state: state, action: B): state {
    if (action.index === 0) return state;
    // more logic
}

Now doSomething works only with variant B of your Action type.

Related