Reputation: 1481
I am trying to use Typescript with Redux, and design them according to Redux Ducks. I am struggling with this typecheck here and I cannot figure out why.
const UPDATE = "ticTacToe/board/update";
const RESET = "ticTacToe/board/reset";
export default function reducer(
state = initialState,
action: IBoardActions
): IBoardState {
switch (action.type) {
case UPDATE:
const { nextPlayer, clickedIndex } = action;
// TypeScript error: Property 'nextPlayer' does not exist on type
//'{ type: string; nextPlayer: string; clickedIndex: number; } | { type: string; }'. TS2339
const newSquares = [...state.squares];
newSquares[clickedIndex] = nextPlayer;
return {
squares: newSquares,
oIsNext: !state.oIsNext
};
case RESET:
return initialState
default:
return state;
}
}
type BoardAction = typeof updateBoard | typeof resetBoard;
export type IBoardActions = ReturnType<BoardAction>;
export function updateBoard(nextPlayer: string, clickedIndex: number) {
return {
type: UPDATE,
nextPlayer,
clickedIndex
};
}
export function resetBoard() {
return {
type: RESET
};
}
I have tried to fix it with enum
, but this doesn't work as well. Is that the problem of switch
?
enum Action {
UPDATE = 'update',
RESET = 'reset'
}
export default function reducer(
state = initialState,
action: IBoardActions
): IBoardState {
switch (action.type) {
case Action.UPDATE:
const { nextPlayer, clickedIndex } = action;
//TypeScript error: Property 'nextPlayer' does not exist on type '{ type: Ac
tion; nextPlayer: TicTacToeSymbols; clickedIndex: number; } | { type: Acti
on; }'. TS2339
const newSquares = [...state.squares];
newSquares[clickedIndex] = nextPlayer;
return {
squares: newSquares,
oIsNext: !state.oIsNext
};
case Action.RESET:
return initialState
default:
return state;
}
}
type BoardAction = typeof updateBoard | typeof resetBoard;
export type IBoardActions = ReturnType<BoardAction>;
export function updateBoard(nextPlayer: TicTacToeSymbols, clickedIndex: number) {
return {
type: Action.UPDATE,
nextPlayer,
clickedIndex
};
}
export function resetBoard() {
return {
type: Action.RESET
};
}
Why am I getting this error? It seems like my typing is correct based on the error message.
Upvotes: 0
Views: 73
Reputation: 44326
We don't really encourage the use of discriminated unions any more as they have their own set of problems and require writing a lot of extra code.
The official recommendation is to use the official redux toolkit which has TypeScript support in mind and will reduce your code drastically. Please see the official redux tutorial on modern redux for a short introduction and then take a look at the essentials tutorial for more in-depth information.
Upvotes: 0
Reputation: 1396
You can also use interface like this.
export interface IBoardActions extends ReturnType<typeof updateBoard>, ReturnType<typeof resetBoard>
Upvotes: 0
Reputation: 5625
You're looking for discriminated unions. Note that the type of IBoardActions
in your snippet is-
{ type: string; nextPlayer: string; clickedIndex: number; } | { type: string; }
But in reality, you want-
{ type: typeof UPDATE; nextPlayer: string; clickedIndex: number; } | { type: typeof RESET; }
This speaks explicitly that the first alternative is only used when the type
property has the same type as that of UPDATE
, and similarly for RESET
.
But in your case, both UPDATE
and RESET
are of type string
. There's no way to discriminate them.
This is where string literal types come in, change your definition of UPDATE
and RESET
to-
const UPDATE = "ticTacToe/board/update" as const;
const RESET = "ticTacToe/board/reset" as const;
The as const
syntax is known as const assertions. If you notice, the type of UPDATE
has now become a string literal set to its value, same for RESET
.
Here's a playground demo
Upvotes: 1