-
-
Notifications
You must be signed in to change notification settings - Fork 15.3k
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
Add state type to dispatch interface #1537
Conversation
Looks OK. The Wouldn't this play along with |
Sorry, I don't have much time right now to do a review. I will check back in a few days. |
export type Dispatch = (action: any) => any; | ||
export interface Dispatch<S> { | ||
<A extends Action>(action: A): A; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here Dispatch<S>
is defined as a paramtrized type (generic) but the time parameter from signature is not used. @Igorbek can you explain the purpose?
Also I think that after a rather long discussion we have decided to drop type parameters from <A extends Action>(action: A): A;
due to middleware issues.
See my initial proposition -> https://gist.github.com/ulfryk/69981ccfb488647b6ee3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having state type parameter S
allows middlewares be more accurate in typings. For example, thunk function takes a dispatch and a getState(): S
, so without S
it couldn't be well-typed. See the example of this here below https://github.com/Igorbek/redux/blob/ts-def-improv/test/typescript/dispatch.ts#L9-L16
Regarding second question, I think this signature express exactly what initial (without midllewares) dispatch does. It take an action and returns it. If I used less generic signature (action: Action) => Action
, due to TypeScript rules it would fail on object literals, such as dispatch({ type: 'ADD_TODO', text: 'todo' })
(Object literals may only specify known properties). Moreover, it returns more precise result - typed action that was passed.
Would the external module augmentation use case work for multiple middlewares? Unfortunately, I don't have time to test for myself right now. |
It will. Module can be augmented by any number of external modules. -----Original Message----- Would the external module augmentation use case work for multiple middlewares? |
@Igorbek How would the final type definition for dispatch look if you say combined redux-thunk and redux-promise (just as an example with easy signatures, I know that using promise when you have thunk is pretty useless)? |
Sure. It might be like that: // node_modules/redux-promise/index.d.ts
declare module "redux" {
export interface Dispatch<S> {
(promiseAction: Promise<Action>): Promise<Action>; // it supports promise of action
<T>(fsa: { payload: Promise<T> }): Promise<T>; // it supports Flux standard actions
}
}
// app.ts
import * as redux from "redux";
import "redux-promise";
import "redux-thunk"; // why not
...
store.dispatch(ajax("...").then(d => ...)); To be honest, these signatures might not be able to express more complicated scenarios, like when promise resolves to other prmoise or a thunk function. It's still possible but would be slightly harder to use or define. |
Thanks @Igorbek, @use-strict |
Out in 3.4.0. |
I acknowledge that this is somewhat late, but: I'm working on upgrading several applications and libraries to the new version of redux because we like dropping dependencies on DefinitelyTyped type packages when However, the Is there any way that |
@AndyMoreland I agree that this is not very convenient in some cases, but it is the best (strongest) we could come up with. I too don't use // SomeComponent.tsx
import {Dispatch} from 'redux'
connect(
store => {...},
(dispatch: Dispatch<MyState>) => {...}
) or having a concrete // configureStore.ts
import {ThunkDispatch} from 'redux-thunk'
export type MyDispatch = Dispatch | ThunkDispatch<MyState>;
// SomeComponent.tsx
import {MyDispatch} from '../configureStore'
connect(
store => {...},
(dispatch: MyDispatch) => {...}
) Therefore it seems to me that having state type parameter for There are also other options, you could have something like this in your code: import {Dispatch as ReduxDispatch} from 'redux';
export type Dispatch = ReduxDispatch<any>; Or wait for optional type parameters (microsoft/TypeScript#2175), although it doesn't have any milestone set. |
@AndyMoreland I'd like to add that you're not late; I think everyone agrees that our goal is to bring the most convenient typings, so your feedback is appreciated. |
This PR is an alternate to #1526
I've based it on what @use-strict proposed and augmented with the following:
Dispatch
interface be parameterized with state typeS
. Because dispatch is a part of store which has a state type, we can do it.Dispatch
to be an interface, so that allows to augment it in other modules, such as middlewares.Dispatch
exposes only one call signature at the the moment. It takes an action and returns it. It aligned with default behavior ofredux
. Any extension middleware could extend it by adding other signatures. For instance,redux-thunk
can add a call signature that takes a function, andredux-promise
can add one that takes a promise. So that any end interface shape will be determined by set of modules imported. This "module augmentation" approach is shown indispatch.ts
test.Middleware
now is a function that returns a function that gets a dispatch and returns new dispatch.Definitions have been tested with TypeScript 1.8.7.