Reputation: 746
I'm trying to use Redux with TypeScript in a little learning project, following this tutorial: https://redux.js.org/recipes/usage-with-typescript/
const INITIAL_STATE: TweetsState = {
tweets: []
};
const tweetsReducer: Reducer<TweetsState> = (
state = INITIAL_STATE,
action: TweetAction
): TweetsState => {
switch (action.type) {
case GET_TWEETS:
return {
...state
};
case TWEETS_RECEIVED:
return {
tweets: [...state.tweets, action.payload]
};
default:
return state;
}
};
My reducer expects an action of type TweetAction, which is the union of two interfaces:
export interface IGetTweetsAction {
type: typeof GET_TWEETS;
}
export interface ITweetsReceivedAction {
type: typeof TWEETS_RECEIVED;
payload: ITweet[];
}
export const GET_TWEETS: string = "GET_TWEETS";
export const TWEETS_RECEIVED: string = "TWEETS_RECEIVED";
export type TweetAction = IGetTweetsAction | ITweetsReceivedAction;
However, within my reducer, I get the error Property 'payload' does not exist on type 'TweetAction'
which is confusing because it suggests the union hasn't worked?
But this is further confusing because within my actions file it clearly allows me to return an object, of type TweetAction, with a payload:
import {
TweetAction,
GET_TWEETS,
TWEETS_RECEIVED,
ITweet
} from "./tweets.types";
export const getTweets = (): TweetAction => ({
type: GET_TWEETS
});
export const tweetsReceived = (tweets: ITweet[]): TweetAction => ({
type: TWEETS_RECEIVED,
payload: tweets
});
I'm aware the code is currently a bit of a mess (I sometimes instinctively prefix interfaces with I being a C# dev, sorry!) as I haven't been through a refactory stage yet as I'm just trying to whip something up quickly, but I've been stumped by this error.
Can anyone see what's going wrong? It's probably something straight forward but the isolation might be getting to my head.
Thanks!
Edit: Note that removing the typesafety and changing:
action: TweetAction
to just action
allows me to do action.payload
and the functionality works fine...
Upvotes: 2
Views: 4957
Reputation: 851
-- edit -----
Ok So apparently you need to remove the : string
typing in those lines:
export const GET_TWEETS: string = "GET_TWEETS";
export const TWEETS_RECEIVED: string = "TWEETS_RECEIVED";
-- /edit -----
I'm pretty new to typescript too, but as I understand it, since you use action.payload
in your reducer, the type TweetAction
is not accepted since it could imply a IGetTweetsAction
type, which does not define a payload
property.
You need to use a more generic type like:
type TweetAction {
type: // ...,
}
to say that you can have an action that will always have a type
and might have some other props (such as a payload
).
As for your confusing example, I think that actually makes sense:
export const getTweets = (): TweetAction => ({
type: GET_TWEETS
});
No payload
is defined so it matches IGetTweetsAction
.
export const tweetsReceived = (tweets: ITweet[]): TweetAction => ({
type: TWEETS_RECEIVED,
payload: tweets
});
While this one matches ITweetsReceivedAction
.
Upvotes: 1