-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Example with Typescript, react-redux, and redux-thunk #213
Comments
We're planning on moving the types out of the package and into DefinitelyTyped, so this will be on the community to provide. For now, the tests will be helpful. |
@timdorr sounds great and I am looking forward to it. If the solution happens to be easy for you, the main thing I am stuck on is how to return a promise in the action.
then in my component I have a prop interface:
Then:
I am getting this typescript error: FYI I am going to post this question to stack overflow as well. |
asked this on Stack overflow, but no luck so far. https://stackoverflow.com/questions/51898937/returning-a-promise-in-a-redux-thunk-with-typescript @timdorr I know you plan to move the typing to Definitely Typed, but perhaps including an Async function in the typescript.ts tests is a good idea? |
@jfbloom22 Did you maybe found a solution? |
@mario-jerkovic no, I still have not found a solution. And I have not received any answers on stack overflow either. It seems strange to me that more people are not using redux-thunk with promises and typescript. |
@jfbloom22 Any luck figuring this out yet or did you end up switching away from redux-thunk? |
This should work: import { ThunkAction, ThunkDispatch } from 'redux-thunk';
import React from 'react';
import { connect } from 'react-redux';
import { Action } from 'redux';
type MyRootState = {};
type MyExtraArg = undefined;
type MyThunkResult<R> = ThunkAction<R, MyRootState, MyExtraArg, Action>;
// Next Line:
// It is important to use Action as last type argument, does not work with any.
type MyThunkDispatch = ThunkDispatch<MyRootState, MyExtraArg, Action>;
const anotherThunkAction = (): MyThunkResult<Promise<boolean>> => (dispatch, getState) => {
return Promise.resolve(true);
};
export interface IProps {
anotherThunkAction: () => Promise<boolean>;
}
export class Foo extends React.Component<IProps> {
componentDidMount() {
this.props.anotherThunkAction().then(value => {
console.log('hello world, got', value);
});
}
render() {
return null;
}
}
const mapDispatchToProps = (dispatch: MyThunkDispatch) => ({
anotherThunkAction: () => dispatch(anotherThunkAction()),
});
export default connect(
() => undefined,
mapDispatchToProps
)(Foo); the action type argument can also be like the test. redux-thunk/test/typescript.ts Line 8 in dc5fc22
Sidenote: Line 4 in dc5fc22
|
I was able to get this to work with @laat 's suggestion above. Thanks! But good grief this is a lot of typing. In my original question I was using "typeof" in my "Iprops" interface. This allowed me to add types to the params in my action function a single time and get the benefit of autocomplete params throughout my project. With this implementation, I am adding types to the params 3 times. Once in the original function, once in "Iprops", and then again in "mapDispatchToProps". Is this the best we can do? To Illustrate where I am adding types to the params 3 times:
|
We can reduce the duplication a bit, with new code in the master branch. But until @timdorr releases a new version with the types from #224, we still have to add type-definitions ourself:
// Remove when https://github.com/reduxjs/redux-thunk/pull/224 is released.
import { Dispatch } from "redux";
import { ThunkAction } from "redux-thunk";
declare module "redux" {
function bindActionCreators<M extends ActionCreatorsMapObject<any>>(
actionCreators: M,
dispatch: Dispatch
): {
[N in keyof M]: ReturnType<M[N]> extends ThunkAction<any, any, any, any>
? (...args: Parameters<M[N]>) => ReturnType<ReturnType<M[N]>>
: M[N]
};
} then we can write mapDispatchToProps from the previous solution like so: import { bindActionCreators } from "redux";
const mapDispatchToProps = (dispatch: MyDispatch) =>
bindActionCreators({ anotherThunkAction }, dispatch); The duplication of types in If you are unconcerned about coupling your components to thunks, it is possible with a utility type: type BoundThunk<
T extends (...args: any[]) => ThunkAction<any, any, any, any>
> = (...args: Parameters<T>) => ReturnType<ReturnType<T>>;
export interface IProps {
// tight coupling
anotherThunkAction: BoundThunk<typeof anotherThunkAction>;
} |
@laat this bindActionCreators() function and new types will be awesome. And thanks for the suggestion, I am going to think about how to decouple my components. In the meantime thanks for the utility type. This gives me something to use and something to study in order to upgrade my Typescript skills. It continues to be very rewarding as I learn more about Typescript and how to use it well. |
It's been some time, was this ever fixed? |
I've got examples of working with Redux Toolkit, thunks, and React-Redux in the Redux Toolkit "Advanced Tutorial" docs page. |
@jfbloom22 @oisinlavery Below works for me:
|
will this ever be improved? as long as the solution to such a common problem remains this verbose (and difficult to find examples for, since most tutorials are overly simple counter or todo apps) I don't think many people are gonna actually do it i'm having that debate myself right now, and frankly i'm leaning towards just clobbering anything to do with redux-thunk with any just to avoid the error spam the effort to payoff ratio just isn't very good here |
@rossPatton : as I linked a couple comments above, the Redux Toolkit "Advanced Tutorial" docs page shows a recommended approach to working with thunks in TS. We also have an example in the Redux docs "Usage with TypeScript" page. |
(1) Is it possible to add a little more detail to the discussion of TS & thunks in the Redux Toolkit docs? Particularly in dealing with the typing inside mapDispatch and useDispatch. For example, in the docs it suggests:
While it would deal fine with
Which gives the correctly shows (2) Furthermore, I can't for the life of me get properly typechecked actions when using redux-thunk & the object form of mapDispatchToProps [though the functional form works fine]. This seems problematic... Can see others also having this issue in bottom of comments @ https://gist.github.com/milankorsos/ffb9d32755db0304545f92b11f0e4beb Thank you for your minimal example posted above! If I may suggest a small change, where you use
|
PRs to improve the docs are always appreciated, yeah. Personally, I've never found restrictions on the type of |
For guys using hooks, this may be useful. I struggled with proper types for useDispatch(). It seems like by default it has any type. I am not 100% sure if this is correct solution and would greatly appreciate any comments / suggestions.
In actions.ts
|
After thinking about it a little longer, as well as trying to get the 100% type-safety to work while using createSlice and failing to get the string literal type right, as documented, I think i've come around to your side. I suppose that given you are always using action creators and they are properly typed, there is little to no need for type safety on the actual dispatch. The only way you could go wrong is by somehow passing in an argument to dispatch that conforms to the generic action expectations, but also isn't the argument you intended to pass in? Seems improbable... Is that your line of thinking when you say you don't find it useful? |
Mostly, yeah. Also, the notion of limiting what can be dispatched is kind of faulty to begin with. Conceptually, dispatching actions is much like triggering an event emitter. You don't know for sure what callbacks may be registered for what actions, and it's legal to emit an event that has no listeners registered at all. In the same way, it's entirely fine to dispatch an action that results in 0 state updates, because no reducers actually cared about it. This kind of goes along with our Style Guide recommendation to "model actions as events". If you think of it that way, the notion of saying "this is an invalid action to dispatch" goes out the window. Any action is legal to dispatch, it's just a question of whether any reducer or middleware logic actually cares about that action. There probably are still cases where a union of actions may be useful (I think It's worth noting that my views on TypeScript are strongly pragmatic, not absolutist. I'm sold on using TS on every project I work on, and do want as much of my codebase to be typed as possible. At the same time, much like unit testing and code coverage, getting that last 20% covered takes significantly more time and effort than the first 80%, and the marginal benefit of doing so goes down considerably. Because of that, I'm not worried about "trying to have every single line of my codebase 100% perfectly typed and preventing all impossible bits of behavior", as I've seen many people try to do. I'm fine with inserting some manual type declarations to bridge the gaps between pieces of code that aren't quite connected from the compiler's point of view, or strategically using So, for my own apps, I'd want to ensure that my reducers, components, thunks, and util functions are correctly typed, with any action creators deriving their types from the reducers (ie, |
So glad to see redux-thunk has great support for Typescript and Redux v4. Where is the best place to view an up to date example of a correctly typed reducer, actions, and a connected component with redux-thunk? This test is a good start: https://github.com/reduxjs/redux-thunk/blob/master/test/typescript.ts
But I can not seem to find an up to date example of what a correctly typed connected component should look like. Specifically I am looking for something that shows mapStateToProps, mapDispatchToProps, and the props interface for the component.
The text was updated successfully, but these errors were encountered: