Reputation: 35
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
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:
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.
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