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 type support #79

Open
jadbox opened this issue Aug 18, 2018 · 13 comments
Open

typescript type support #79

jadbox opened this issue Aug 18, 2018 · 13 comments
Labels
enhancement New feature or request

Comments

@jadbox
Copy link

jadbox commented Aug 18, 2018

It would be great to have a type definition file for the project!

@didierfranc didierfranc added the enhancement New feature or request label Aug 22, 2018
@SaphuA
Copy link

SaphuA commented Sep 21, 2018

If @didierfranc isn't planning on updating typings with every release it might be better to move them to https://github.com/DefinitelyTyped/DefinitelyTyped

@SaphuA
Copy link

SaphuA commented Sep 24, 2018

We're halfway there, but there's some work to be done. Especially with the prop-type of the consuming components.

declare module "react-waterfall" {
    import { Config, Store } from "react-waterfall/types";

    const rw: ReactWaterfall;

    interface ReactWaterfall {
        <S, A>(config: Config<S, A>, middlewares?: any[]): Store<S, A>;
    }

    export = rw;
}

declare module "react-waterfall/types" {
    import * as React from "react";

    export type Config<S, A> = {
        initialState: S,
        actionsCreators: {
            [K in keyof A]: (state: S, actions: A, ...args: any[]) => Partial<S> | Promise<Partial<S>>
        }
    }

    export type Connect<S> = (selector: (state: S) => any) => (baseComponent: React.ComponentType<any>) => React.ComponentType<any>

    export type Store<S, A> = {
        Provider: any,
        connect: Connect<S>,
        actions: A,
        subscribe: any,
        unsubscribe: any,
    }
}

Note that we had to separate the types in a different namespace and overwrite the export, because of the way react-waterfall does its export.

@chrismcleod
@FlorianRappl
@VanthiyaThevan

Do you guys have any suggestions?

@FlorianRappl
Copy link

FlorianRappl commented Sep 24, 2018

Yep I definitely would like to contribute @SaphuA. I'll try to allocate some time within this week if someone else is faster please indicate 🍻 - appreciated!

@FlorianRappl
Copy link

Alright I finally had to chance to look at it (sorry for the delay!).

This is what I came up with based on @SaphuA s great work. Notable extensions: Strong typing for the component (i.e., TS will ensure that you really "connect" to the state also from a typings perspective). Less use of any.

declare module 'react-waterfall' {
  import { Config, Store } from 'react-waterfall/types';

  const rw: ReactWaterfall;

  interface ReactWaterfall {
    <S, A>(config: Config<S, A>, middlewares?: any[]): Store<S, A>;
  }

  export default rw;
}

declare module 'react-waterfall/types' {
  import * as React from 'react';

  type RemoveArgumentTypes<S, A, T> = T extends (state: S, actions: A, ...args: infer U) => infer R ? U : any;

  export interface Config<S, A> {
    initialState: S;
    actionsCreators: { [K in keyof A]: (state: S, actions: A, ...args: any[]) => Partial<S> | Promise<Partial<S>> };
  }

  export interface Connect<S> {
    <P, K>(selector: (state: S) => K): (baseComponent: React.ComponentType<P & K>) => React.ComponentType<P>;
  }

  export type Store<S, A> = {
    Provider: React.ComponentType;
    connect: Connect<S>;
    actions: { [K in keyof A]: (...a: RemoveArgumentTypes<S, A, A[K]>) => void };
    subscribe: any;
    unsubscribe: any;
  };
}

It's not completely finished. I'll update the post once I have it in a state that I'm satisfied with.

Question though @didierfranc: Should the d.ts be part of this repo or is an "@types/" publication the new preference? Personally, I would love to have them in here - but I do of course understand the maintenance argument.

@DimitryDushkin
Copy link

How do u use these types? I've tried and it failed on actual store creation.

@FlorianRappl
Copy link

FlorianRappl commented Oct 29, 2018

@DimitryDushkin not sure what failed without some example code. Just place the snippet above in a d.ts file in repository (e.g., in src/modules.d.ts).

I think something like the following should work (just took the standard example and added some types for being explicit - note: many of them may be omitted / are inferred in practice):

import createStore from 'react-waterfall';

export interface State {
  count: 0;
}

const initialState: State = { count: 0 };

export const { Provider, connect, actions } = createStore({
  initialState,
  actionsCreators: {
    increment({ count }: State): Partial<State> {
      return { count: count + 1 };
    },
  },
});

Note that I am missing in the config directly in the store - that way you ensure that TS correctly infers the type. Otherwise it needs to infer it from some variable directly, which may not work reliably depending on the conditions.

@SaphuA
Copy link

SaphuA commented Nov 2, 2018

@FlorianRappl the RemoveArgumentTypes with infer is pretty brilliant! Great job.

Small improvement to change:
connect: Connect<S>;
To:
connect: Connect<Partial<S>>;

@DimitryDushkin
Copy link

DimitryDushkin commented Dec 28, 2018

@FlorianRappl what type will be in second argument of action? I mean

export const { Provider, connect, actions } = createStore({
  initialState,
  actionsCreators: {
    increment({ count }: State, actions: ???): Partial<State> {
      return { count: count + 1 };
    },
  },
});

Also looks like

actions: { [K in keyof A]: (...a: RemoveArgumentTypes<S, A, A[K]>) => void };

is not quite correct, it should be more like

actions: { [K in keyof A]: (...a: RemoveArgumentTypes<S, A, A[K]>) => ReturnType<A[K]> };

but still type of arguments of actions is any. I'm trying to resolve it.

@scerelli
Copy link

any update on this issue? I saw that there's no published type on definitelytyped do you still need help with this?

@DimitryDushkin
Copy link

I've made quite similar library — https://github.com/DimitryDushkin/react-localux — it is written on TS and have thunk-like functionality built-in. That's some kind of update.)

@jtrujill
Copy link

I've modified the above to fix some of the type issues that I was having. Along with defining your own typeRoots, you'll need to add a paths variable pointing to your typings root.

"paths": {
      "*": ["./typings/*"]
    }
import * as React from 'react';

export = ReactWaterfall;

declare function ReactWaterfall<S, A>(config: ReactWaterfall.Config<S, A>, middlewares?: any[]): ReactWaterfall.Store<S, A>;

declare namespace ReactWaterfall {
  type RemoveArgumentTypes<S, A, T> = T extends (state: S, actions: A, ...args: infer U) => infer R ? U : any;

  type Omit<T, K extends keyof any> = T extends any ? Pick<T, Exclude<keyof T, K>> : never;

  type ConsistentWith<DecorationTargetProps, InjectedProps> = {
    [P in keyof DecorationTargetProps]: P extends keyof InjectedProps
      ? InjectedProps[P] extends DecorationTargetProps[P] ? DecorationTargetProps[P] : InjectedProps[P]
      : DecorationTargetProps[P]
  };

  export interface Config<S, A> {
    initialState: S;
    actionsCreators: { [K in keyof A]: (state: S, actions: A, ...args: any[]) => Partial<S> | Promise<Partial<S>> };
  }

  type PropInjector<InjectedProps> = <C extends React.ComponentType<ConsistentWith<React.ComponentProps<C>, InjectedProps>>>(
    component: C
  ) => React.ComponentType<Omit<JSX.LibraryManagedAttributes<C, React.ComponentProps<C>>, keyof InjectedProps>>;

  export interface Connect<S> {
    <InjectedProps>(selector: (state: S) => InjectedProps): PropInjector<InjectedProps>;
  }

  export interface Store<S, A> {
    Provider: React.ComponentType;
    connect: Connect<S>;
    actions: { [K in keyof A]: (...a: RemoveArgumentTypes<S, A, A[K]>) => void };
    subscribe: any;
    unsubscribe: any;
  }

  export type WithProps<T extends (state: S) => any> = ReturnType<T>;
}

If I have time I'll try to fix the actions typings

@DimitryDushkin
Copy link

DimitryDushkin commented May 4, 2019

If I have time I'll try to fix the actions typings

I think it is impossible to fix it. Basically you want something like:

type ExportedAction<F extends Function> = F extends (
  ...args: infer Args
) => (actions: any) => infer R
  ? (...args: Args) => R
  : never;

type StrippedActions<A extends Record<string, Function>> = {
  [K in keyof A]: ExportedAction<A[K]>
};

const exampleActions = {
  action: (arg: string) => (
    actions: StrippedActions<typeof exampleActions>
  ) => {
    actions.action("op");
    return "op";
  }
};
const s = createStore(state, exampleActions);

Which is fine, but looks like it is impossible to write it without explicitly write every action like (actions: StrippedActions<typeof exampleActions>) => , because TS gives error "const used before declaration" on:

const exampleActions: SomeType<typeof exampleActions> = {...}

@sampolahtinen
Copy link

Hey :)

What is the current status of adding types to react-waterfall? Would you need some help?

I would love to try adding some types with some guidance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

8 participants