Skip to content
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

Typescript definition #206

Closed
Keats opened this issue Jul 2, 2015 · 29 comments
Closed

Typescript definition #206

Keats opened this issue Jul 2, 2015 · 29 comments

Comments

@Keats
Copy link

Keats commented Jul 2, 2015

Just an issue to remind me to start doing it as soon as #195 is merged.

Normally that would live in https://github.com/borisyankov/DefinitelyTyped but it doesn't support typescript 1.5 yet so I'll put it here if that's ok, it's also a good way to quickly see the public api of a library imo

@gaearon
Copy link
Contributor

gaearon commented Jul 15, 2015

Would #254 help here, or is “definition” a separate thing?

@chicoxyzzy
Copy link
Contributor

TS definition files are different from Flow definition files and AFAIK they aren't compatible with each other

@Keats
Copy link
Author

Keats commented Jul 15, 2015

They are a bit different.
I got the following for now that I'm editing/changing as I'm using it

declare module "redux" {
    interface ActionCreator {
        (...args: Array<any>): Object;
    }

    interface ActionCreators {
        [key: string]: ActionCreator
    }

    interface Reducer {
        (state: any, action: Object): any;
    }

    interface StoreMethods {
        dispatch(action: Object): Object;
        getState(): Object;
    }

    class Store {
        getReducer(): Function;
        replaceReducer(nextReducer: Reducer): void;
        dispatch(action: Object): Object;
        getState(): Object;
        subscribe(listener: Function): Function;
    }

    function createStore(
        reducer: Reducer | Object,
        initialState?: any
    ): Store;

    function bindActionCreators<T>(
        actionCreators: ActionCreator | ActionCreators,
        dispatch: Function
    ): T;

    function composeMiddleware(...middlewares: Array<Function>): Function;
    function composeReducers(reducers: Array<Reducer>): Reducer;

}

declare module "redux/react" {
    import React = require("react");
    import redux = require("redux");

    interface ProviderProps {
        store: redux.Store;
        children: Function;
    }

    interface ProviderState {
        store: redux.Store;
    }

    class Provider extends React.Component<ProviderProps, ProviderState> {}

    interface ConnectorProps {
        children: Function;
        select: Function;
    }

    class Connector extends React.Component<ConnectorProps, any> {}
}

It's missing some stuff stilll but it's getting there

@Keats
Copy link
Author

Keats commented Jul 21, 2015

Updated a bit (and will likely wait for redux/react-redux to hit 1.0):

declare module "redux" {
    interface ActionCreator {
        (...args: Array<any>): Object;
    }

    interface ActionCreators {
        [key: string]: ActionCreator
    }

    interface Reducer {
        (state: any, action: Object): any;
    }

    interface StoreMethods {
        dispatch(action: Object): Object;
        getState(): Object;
    }

    class Store {
        getReducer(): Function;
        replaceReducer(nextReducer: Reducer): void;
        dispatch(action: Object): Object;
        getState(): Object;
        subscribe(listener: Function): Function;
    }

    function createStore(
        reducer: Reducer | Object,
        initialState?: any
    ): Store;

    function bindActionCreators<T>(
        actionCreators: ActionCreator | ActionCreators,
        dispatch: Function
    ): T;

    function composeMiddleware(...middlewares: Array<Function>): Function;
    function combineReducers(reducers: Object): Reducer;

}

declare module "react-redux" {
    import React = require("react");
    import redux = require("redux");

    interface ProviderProps {
        store: redux.Store;
        children: Function;
    }

    interface ProviderState {
        store: redux.Store;
    }

    class Provider extends React.Component<ProviderProps, ProviderState> {}

    interface ConnectorProps {
        children: Function;
        select: Function;
    }

    class Connector extends React.Component<ConnectorProps, any> {}
}

@wbuchwalter
Copy link
Contributor

Nice job!
Some thoughts:

  • It would be nice to have redux and react-redux distributed separately for those using redux without react.
  • Is there a particular reason you are using Object instead of any?
    For example having Store.getState() returning any instead of Object would allow to do Store.getState().todoReducer without compile time error.

@gaearon
Copy link
Contributor

gaearon commented Jul 23, 2015

It would be nice to have redux and react-redux distributed separately for those using redux without react.

That's how it is in 1.0 RC (and will be in 1.0). The React bindings are split into http://github.com/gaearon/react-redux.

@Keats
Copy link
Author

Keats commented Jul 24, 2015

It should return any indeed
That's still a WIP at this point so i will separate later on, going to work on a small boilerplate with typescript/redux/immutablejs to make sure it all works fine

@wbuchwalter
Copy link
Contributor

Here is a slightly updated version (without react-redux).
Can still be improved, but If you are ok with this, I'll submit a PR on DefinitlyTyped so others can start using it and give us feedback.

declare module Redux {

  interface ActionCreator extends Function {
    (...args: any[]): any;
  }

  interface ActionCreators {
    [key: string]: ActionCreator
  }

  interface Reducer extends Function {
    (state: any, action: any): any;
  }

  interface Dispatch extends Function {
    (action: any): any;
  }

  interface StoreMethods {
    dispatch: Dispatch;
    getState(): any;
  }

  interface MiddlewareArg {
    dispatch: Dispatch;
    getState: Function;
  }

  interface Middleware extends Function {
    (obj: MiddlewareArg): Function;
  }

  class Store {
    getReducer(): Reducer;
    replaceReducer(nextReducer: Reducer): void;
    dispatch(action: any): any;
    getState(): any;
    subscribe(listener: Function): Function;
  }

  function createStore(reducer: Reducer, initialState?: any): Store;
  function bindActionCreators<T>(actionCreators: ActionCreator | ActionCreators, dispatch: Dispatch): T;
  function composeMiddleware(...middlewares: any[]): Function;
  function combineReducers(reducers: any): Reducer;
  function applyMiddleware(...middleware: Middleware[]): Function;
}

declare module "redux" {
  export = Redux;
};

@gaearon
Copy link
Contributor

gaearon commented Jul 29, 2015

@wbuchwalter This looks good to me! One minor change is I don't think bindActionCreators accepts a single action creator. Another change is we're removing composeMiddleware from public API (it was added by accident and there are no use cases for it).

It also would be nice to describe reducer in combineReducers as { [key]: string: Reducer }.

@Keats
Copy link
Author

Keats commented Jul 29, 2015

Looks good !
I believe StoreMethods is not needed and isn't MiddlewareArg only that if you use redux-thunk?

I would wait until 1.0 to submit it on definitelytyped.
In the meantime I started a boilerplate/example in typescript that I'm using to test things out: https://github.com/Keats/react-boilerplate
Not finished yet but would be nice to have feedback, I find it a bit annoying to have to dispatch 2 actions when I add one card for example

@gaearon
Copy link
Contributor

gaearon commented Jul 29, 2015

@Keats MiddlewareArg is not for redux-thunk, it's actually for implementing middleware itself. This is the “thing” middleware receives. redux-thunk just happens to pass it down.

@Keats
Copy link
Author

Keats commented Jul 29, 2015

Yes indeed, I realised after posting, as always. Cheers

@wbuchwalter
Copy link
Contributor

Updated following your suggestions:

declare module Redux {

  interface ActionCreator extends Function {
    (...args: any[]): any;
  }

  interface ActionCreators {
    [key: string]: ActionCreator;
  }

  interface Reducer extends Function {
    (state: any, action: any): any;
  }

  interface Reducers {
    [key: string]: Reducer;
  }

  interface Dispatch extends Function {
    (action: any): any;
  }

  interface StoreMethods {
    dispatch: Dispatch;
    getState(): any;
  }

  interface MiddlewareArg {
    dispatch: Dispatch;
    getState: Function;
  }

  interface Middleware extends Function {
    (obj: MiddlewareArg): Function;
  }

  class Store {
    getReducer(): Reducer;
    replaceReducer(nextReducer: Reducer): void;
    dispatch(action: any): any;
    getState(): any;
    subscribe(listener: Function): Function;
  }

  function createStore(reducer: Reducer, initialState?: any): Store;
  function bindActionCreators<T>(actionCreators: ActionCreators, dispatch: Dispatch): T;
  function combineReducers(reducers: Reducers): Reducer;
  function applyMiddleware(...middleware: Middleware[]): Function;
}

declare module "redux" {
  export = Redux;
}

@Keats Regarding DefinitlyTyped, we can add a README and a prefix to make it clear this is targeting 1.0 if that is your concern.
Several people already asked me about that, and it would be easier to follow progress.
Let me know what you think.

@gaearon
Copy link
Contributor

gaearon commented Jul 29, 2015

Minor change: I just merged a PR that also lets bindActionCreators to accept a single function instead of object (and return a function in this case). The rest looks good to me.

@wbuchwalter
Copy link
Contributor

Seems that Typescript isn't able to understand that when doing import * from './reducers', we are in fact just importing a map, so I deleted ActionCreators and Reducers types in my version, and replaced them by any, which incidentally also handles the case where we pass a single action to bindActionCreators

@Keats
Copy link
Author

Keats commented Jul 29, 2015

@wbuchwalter Yeah Typescript can't guess the type from import * as reducers so you could cast it, which is i think a better solution than removing the types

@wbuchwalter
Copy link
Contributor

The thing is that you could cast the import * to anything you want without the compiler complaining, so that doesn't give you any more protection than any and it will not be obvious for users to know they have to cast since the error will look like type ./src/reducer/reducers.ts is not assignable to type [key: string]

@Lenne231
Copy link

Lenne231 commented Aug 4, 2015

Why are you using any everywhere? Why not generic types:

interface Reducer<S, A> {
  (state: S, action: A): S;
}

@gaearon
Copy link
Contributor

gaearon commented Aug 4, 2015

@Lenne231 Generic types might be tricky right now because of some issues in Redux internal code, see #299 and #290. But that's related to Flow; not sure about TypeScript.

@wbuchwalter
Copy link
Contributor

Same as #299, you can change any by generics in some places, but it takes time to do it well, and also the API is still changing, e.g: bindActionCreators mentioned above and #350 for example.

Also in the case of Reduceryou will need #290 as pointed out by @gaearon otherwise would'nt you have to deal with Reducer<any, any> in a lot of places? e.g.:

CreateStore(reducer: Reducer<any, any>, initialState: any?);
getReducer(): Reducer<any, any>;

And on a side note if Reducer (or other interfaces) expect a typed Action, it would be weird (in my opinion), to expect the user to provide the interface for it. We would need to provide something like

interface Action<T> {
  type: string;
  payload: T;
}

But it's kind of tricky since everyone seems to use his own kind of actions, so which Action structure should we use? there is Flux Standard Action which i think is great but I don't know how much people folllow that and if it should be enforced.

TL;DR: I'm using any because of laziness 😄

@leoasis
Copy link
Contributor

leoasis commented Aug 4, 2015

I've been working on the Flow types for redux here: #356, perhaps that will be helpful since I'm using generics.

Some function apis, like bindActionCreators and combineReducers are very tricky (perhaps even not possible) to correctly type without using any, but if you find out a way to do that please please let me know (discussion about this last thing in Flow: facebook/flow#673).

@wbuchwalter
Copy link
Contributor

Since 1.0.0 is released, there is now a PR on DefinitelyTyped, so I guess we can move our discussion there and close this issue?

@gaearon
Copy link
Contributor

gaearon commented Aug 14, 2015

Sounds good.

@aintnorest
Copy link

We're a few version down the road any have an updated Typescript definition? I've looked around but only found the 1.0.0 version.

@aravantv
Copy link

aravantv commented Dec 6, 2015

Is this now on DefinitelyType? Can't find it... Or where can we access the latest version of your attempts? Just above or...?

@aravantv
Copy link

aravantv commented Dec 9, 2015

thx! I obviously missed it...

@gaearon
Copy link
Contributor

gaearon commented Feb 19, 2016

Please help review the new official typings in #1413.

@MichaelTontchev
Copy link

MichaelTontchev commented Feb 12, 2017

@leoasis I am not sure whether the issue you mention above about typing combineReducers is exactly what I'm thinking or whether it's still relevant, but I recently found this solution with the keyof feature in TypeScript 2.1: microsoft/TypeScript#1213 (comment)

This allows us to create a type-safe (relative to the state type) combineReducers function.

Edit: Created an issue here: #2238

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants