Riya Bhardwaj
Riya Bhardwaj

Reputation: 35

React Redux Actions Types using typeof

in the below, code action type (action as SucceededAction) is specified in the reducer and things are working but if I remove (as SucceededAction) I am getting an error 'payload does not exist on type 'SucceededAction | FailedAction'.'

Now if I replace the constants with --

type_SUCCEEDED:'type_SUCCEEDED',type_FAILED:'type_FAILED' and use them like type_FAILED and type_SUCCEEDED instead of type.FAILED, type.SUCCEEDED, it works without typecasting.

So, in the code below I think typeof is not working for my actions due to which I have to typecast in the reducers. Want to know the exact cause for the same. Please help if you know the reason and help me out here to find out the mistake in the code below.

Thanks.

//constants
export const type = {
    SUCCEEDED: 'type_SUCCEEDED',
    FAILED: 'type_FAILED'
};

export interface SucceededAction {
  readonly type: typeof type.SUCCEEDED;
  readonly payload: string;
}

export interface FailedAction {
  readonly type: typeof type.FAILED;
}

//actions
export const Succeeded = (payload: string): SucceededAction => ({
  type: type.SUCCEEDED,
  payload,
});

export const Failed = (): FailedAction => ({
  type: type.FAILED,
});

//reducer
export const myReducer = (
  state: string = '',
  action:
    | SucceededAction
    | FailedAction,
): string => {
  switch (action.type) {
    case type.SUCCEEDED:
      return (action as SucceededAction) //type specified
        .payload;
    case type.FAILED:
      return state;
    default:
      return state;
  }
};

Upvotes: 3

Views: 353

Answers (1)

Linda Paiste
Linda Paiste

Reputation: 42160

Right now TypeScript sees both type.SUCCEEDED and type.FAILED as string. This means that your action, SucceededAction | FailedAction, is just { readonly type: string; readonly payload: string; } | { readonly type: string; }.

The type names need to be string literal types in order to discriminate the union. This can be done in two ways:

1) Enums

You can make your type variable an enum:

export enum type {
    SUCCEEDED = 'type_SUCCEEDED',
    FAILED = 'type_FAILED'
};

A TypeScript enum is both a value and a type, so you wouldn't need to use typeof anymore. You can use it as type directly, like this:

export interface FailedAction {
  readonly type: type.FAILED;
}

Docs: Enums

This is exactly the sort of situation that string enums are designed for. In my opinion this is the better approach, but there is another solution worth mentioning.

2) as const

You can declare the variable as a literal read-only with as const:

export const type = {
    SUCCEEDED: 'type_SUCCEEDED',
    FAILED: 'type_FAILED'
} as const;

Docs: const assertions

Upvotes: 1

Related Questions