0

I am learning typescript and trying to strong-typed useReducer with reactjs. This is logic for reducer, but typescript is yelling at me that property incStep doesn't exist on Act because it doesn't exists on Decrement.

type Increment = {
    type: string,
    incStep: number
}

type Decrement = {
    type: string,
    decStep: number
}

type State = {
    count: number
}

type Actions = Increment | Decrement

const reducer = (state: State, action: Actions) : State =>  {

    if(action.type == 'Inc') {
        return {count: state.count + action.incStep }
    } else if(action.type == 'Dec') {
        return {count: state.count - action.decStep}
    }

    return state
}

Getting the following error: Property 'incStep' does not exist on type 'Act'. Property 'incStep' does not exist on type 'Dec'.

Now I think Union types mean All or either one.

for example

const action : Actions = {
  type: 'inc',
  incStep: 1,
  decStep: 2'
}

// or

const action : Actions = {
  type: 'inc',
  incStep: 1,
}

// or 

const action : Actions = {
  type: 'dec',
  decStep: 1,
}

Also I know that switch statement is good to use here, but I used if-else as the number of action types were only two.

I know if I use string literal type 'inc' | 'dec', then I have no problem.

Can someone please explain What am I doing wrong?

1 Answers1

3

I think use of discrimination types is ideal here.

You can learn more about discrimination types here

type Increment = {
  type: "Inc"; // this field is the differentiator
  incStep: number;
};

type Decrement = {
  type: "Dec"; // this field is the differentiator
  decStep: number;
};

type State = {
  count: number;
};

type Actions = Increment | Decrement;

const reducer = (state: State, action: Actions): State => {
  switch (action.type) {
    case "Inc":
      return { count: state.count + action.incStep };
    case "Dec":
      return { count: state.count - action.decStep };
    default:
      return state;
  }
};

Using switch case is a better and concise way to write such code as it provides better readability and follows DRY principal.

Shubham Waje
  • 781
  • 7
  • 12
  • Exactly what I needed. Thanks. So what I'm understanding is **discrimination type** is used to narrow down the types in a union by using a single type property as a literal type. And thus I think in my case the as the type is common but since it is a string, and both types can be possible. – Amit Dhaterwal Feb 13 '23 at 13:16
  • 1
    Yes. In **discriminated types** you must have one property, having `literal type` based on which you can differentiate. typing it as `string` does not provide that much of type safety and auto complete assistance as `literal types` does. – Shubham Waje Feb 13 '23 at 13:24