use-reducer-with-side-effects icon indicating copy to clipboard operation
use-reducer-with-side-effects copied to clipboard

Typings

Open kennetpostigo opened this issue 5 years ago • 12 comments

Is there anyway we can get some typescript definitions for this library?

kennetpostigo avatar Mar 08 '20 22:03 kennetpostigo

iirc @chrisdhanaraj has some he may be able to share

jamesplease avatar Mar 09 '20 03:03 jamesplease

Yes! I'll open a PR tomorrow to definitely typed and cc you both

chrisdhanaraj avatar Mar 09 '20 03:03 chrisdhanaraj

@chrisdhanaraj any chance I can get a copy of your types to add to my local type defs?

kennetpostigo avatar Mar 12 '20 21:03 kennetpostigo

I found the link he sent to me with the typings, but I’ll need to wait til tomorrow to post em here as I’m not on my work computer.

jamesplease avatar Mar 13 '20 02:03 jamesplease

These may not be for the latest version of the lib, but here's what Chris sent me a few months back:

/* eslint-disable @typescript-eslint/no-explicit-any */

declare module 'use-reducer-with-side-effects' {
  import { Dispatch } from 'react';
  // useCreateReducerWithEffect has a different signature than the original reducer
  // so need to redeclare each of these properties

  type Reducer<S, A> = (prevState: S, action: A) => ReducerReturn<S, A> | symbol;
  type ReducerState<R extends Reducer<any, any>> = R extends Reducer<infer S, any> ? S : never;
  type ReducerAction<R extends Reducer<any, any>> = R extends Reducer<any, infer A> ? A : never;

  export type ReducerReturnBeta<S, A> =
    | UpdateReturn<S>
    | symbol
    | SideEffectReturn<S, A>
    | UpdateWithSideEffectReturn<S, A>;

  export interface ReducerReturn<S, A> {
    newState?: S;
    newSideEffect?: SideEffectCallback<S, A>;
  }

  export type SideEffectCallback<S, A> = (state: S, dispatch: Dispatch<A>) => void;

  export interface UpdateReturn<S> {
    newState: S;
  }

  export function Update<S>(newState: S): UpdateReturn<S>;

  export function NoUpdate(): symbol;

  export interface SideEffectReturn<S, A> {
    newSideEffect: SideEffectCallback<S, A>;
  }
  export function SideEffect<S, A>(state: S, dispatch: Dispatch<A>): SideEffectReturn<S, A>;

  export interface UpdateWithSideEffectReturn<S, A> {
    newState: S;
    newSideEffect: SideEffectCallback<S, A>;
  }

  export interface DefaultInitState<I> {
    sideEffects: [];
    state: I;
  }

  export function UpdateWithSideEffect<S, A>(
    newState: S,
    newSideEffect: SideEffectCallback<S, A>
  ): UpdateWithSideEffectReturn<S, A>;

  export default function useCreateReducerWithEffect<R extends Reducer<any, any>, I>(
    reducer: R,
    initializerArg: I,
    initializer?: undefined
  ): [ReducerState<R>, Dispatch<ReducerAction<R>>];

  export default function useCreateReducerWithEffect<R extends Reducer<any, any>, I>(
    reducer: R,
    initializerArg: I,
    initializer: (init: DefaultInitState<I>) => DefaultInitState<I>
  ): [ReducerState<R>, Dispatch<ReducerAction<R>>];
}

jamesplease avatar Mar 13 '20 21:03 jamesplease

Ah sorry! These are actually very bad - I'll be out of work and will send these in an hour

On Fri, Mar 13, 2020 at 2:57 PM, James < [email protected] > wrote:

These may not be for the latest version of the lib, but here's what Chris sent me a few months back:

/* eslint-disable @typescript-eslint/no-explicit-any */ declare module ' use-reducer-with-side-effects ' { import { Dispatch } from ' react ' ; // useCreateReducerWithEffect has a different signature than the original reducer // so need to redeclare each of these properties type Reducer < S , A > = ( prevState : S , action : A ) => ReducerReturn < S , A > | symbol ; type ReducerState < R extends Reducer < any , any >> = R extends Reducer < infer S , any > ? S : never ; type ReducerAction < R extends Reducer < any , any >> = R extends Reducer < any , infer A > ? A : never ; export type ReducerReturnBeta < S , A > = | UpdateReturn < S > | symbol | SideEffectReturn < S , A > | UpdateWithSideEffectReturn < S , A

; export interface ReducerReturn < S , A > { newState ? : S ; newSideEffect ? : SideEffectCallback < S , A >; } export type SideEffectCallback < S , A > = ( state : S , dispatch : Dispatch < A >) => void ; export interface UpdateReturn < S > { newState : S ; } export function Update< S >( newState : S ) : UpdateReturn < S >; export function NoUpdate() : symbol ; export interface SideEffectReturn < S , A > { newSideEffect : SideEffectCallback < S , A >; } export function SideEffect< S , A >( state : S , dispatch : Dispatch < A ) : SideEffectReturn < S , A >; export interface UpdateWithSideEffectReturn < S , A > { newState : S ; newSideEffect : SideEffectCallback < S , A >; } export interface DefaultInitState < I > { sideEffects : []; state : I ; } export function UpdateWithSideEffect< S , A >( newState : S , newSideEffect : SideEffectCallback < S , A > ) : UpdateWithSideEffectReturn < S , A >; export default function useCreateReducerWithEffect< R extends Reducer < any , any >, I >( reducer : R , initializerArg : I , initializer ? : undefined ) : [ ReducerState < R >, Dispatch < ReducerAction < R >>]; export default function useCreateReducerWithEffect< R extends Reducer < any , any >, I >( reducer : R , initializerArg : I , initializer : ( init : DefaultInitState < I >) => DefaultInitState < I > ) : [ ReducerState < R >, Dispatch < ReducerAction < R >>]; }

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub ( https://github.com/conorhastings/use-reducer-with-side-effects/issues/36#issuecomment-598933320 ) , or unsubscribe ( https://github.com/notifications/unsubscribe-auth/AAJVCXUSSLQWMSXAHB5AN7LRHKT4VANCNFSM4LD5SPBA ).

chrisdhanaraj avatar Mar 13 '20 21:03 chrisdhanaraj

@jamesplease Thank you! Will use those for now! 🙏

kennetpostigo avatar Mar 14 '20 18:03 kennetpostigo

@kennetpostigo I AM SO SORRY, oh my god, try these on

declare module "use-reducer-with-side-effects" {
  export type ReducerReturn<ReducerState, ReducerActions> =
    | symbol
    | UpdateReturn<ReducerState>
    | SideEffectReturn<ReducerState, ReducerActions>
    | UpdateWithSideEffectReturn<ReducerState, ReducerActions>;

  export type Reducer<ReducerState, ReducerActions> = (
    state: ReducerState,
    action: ReducerActions
  ) => ReducerReturn<ReducerState, ReducerActions>;

  export type Dispatch<ReducerActions> = (action: ReducerActions) => void;

  export interface StateShape<ReducerState> {
    sideEffects: any[];
    state: ReducerState;
  }

  export type CancelFunction = () => void;
  export type SideEffectCallback<ReducerState, ReducerActions> = (
    state: ReducerState,
    dispatch: Dispatch<ReducerActions>
  ) => void | CancelFunction;

  export function NoUpdate(): symbol;

  export interface SideEffectReturn<ReducerState, ReducerActions> {
    sideEffects: SideEffectCallback<ReducerState, ReducerActions>[];
  }

  export function SideEffect<ReducerState, ReducerActions>(
    args: SideEffectCallback<ReducerState, ReducerActions>
  ): SideEffectReturn<ReducerState, ReducerActions>;

  export interface UpdateReturn<ReducerState> {
    state: ReducerState;
  }

  export function Update<ReducerState>(
    state: ReducerState
  ): UpdateReturn<ReducerState>;

  export interface UpdateWithSideEffectReturn<ReducerState, ReducerActions> {
    state: ReducerState;
    sideEffects: SideEffectCallback<ReducerState, ReducerActions>[];
  }

  export function UpdateWithSideEffect<ReducerState, ReducerActions>(
    state: any,
    sideEffects: SideEffectCallback<ReducerState, ReducerActions>
  ): UpdateWithSideEffectReturn<ReducerState, ReducerActions>;

  export default function useCreateReducerWithEffect<
    ReducerState,
    ReducerActions
  >(
    reducer: Reducer<ReducerState, ReducerActions>,
    initializerArg: ReducerState,
    initializer?: (args: StateShape<ReducerState>) => StateShape<ReducerState>
  ): [ReducerState, Dispatch<ReducerActions>];
}

chrisdhanaraj avatar Mar 14 '20 18:03 chrisdhanaraj

Thank you @chrisdhanaraj!

kennetpostigo avatar Mar 14 '20 19:03 kennetpostigo

I added type definitions to the library itself in a PR and I currently use the generated definitions in one of my projects:

import { Dispatch } from "react"

declare module "use-reducer-with-side-effects" {
  export declare type ReducerWithSideEffects<S, A> = (
    prevState: S,
    action: A | NoUpdateSymbol
  ) => Partial<StateWithSideEffects<S, A>> | NoUpdateSymbol
  export declare type StateWithSideEffects<S, A> = {
    state: S
    sideEffects: SideEffect<S, A>[]
  }
  export declare type SideEffect<S, A> = (
    state: S,
    dispatch: Dispatch<A>
  ) => Promise<void> | void | CancelFunc<S>
  export declare type CancelFunc<S> = (state: S) => void
  export declare type NoUpdateSymbol = typeof NO_UPDATE_SYMBOL
  export declare const NO_UPDATE_SYMBOL: unique symbol
  export declare const Update: <S>(
    state: S
  ) => {
    state: S
  }
  export declare const NoUpdate: () => typeof NO_UPDATE_SYMBOL
  export declare const UpdateWithSideEffect: <S, A>(
    state: S,
    sideEffects: SideEffect<S, A>[]
  ) => {
    state: S
    sideEffects: SideEffect<S, A>[]
  }
  export declare const SideEffect: <S, A>(
    sideEffects: SideEffect<S, A>[]
  ) => {
    sideEffects: SideEffect<S, A>[]
  }
  export declare function executeSideEffects<S, A>({
    sideEffects,
    state,
    dispatch,
  }: {
    sideEffects: SideEffect<S, A>[]
    state: S
    dispatch: Dispatch<A>
  }): Promise<CancelFunc<S>[]>
  export declare function mergeState<S, A>(
    prevState: StateWithSideEffects<S, A>,
    newState: Partial<StateWithSideEffects<S, A>> | NoUpdateSymbol,
    isUpdate: boolean
  ): StateWithSideEffects<S, A>
  export default function useCreateReducerWithEffect<S, A>(
    reducer: ReducerWithSideEffects<S, A>,
    initialState: S,
    init?: (state: S) => Partial<StateWithSideEffects<S, A>>
  ): [S, Dispatch<A | NoUpdateSymbol>]
  export declare function composeReducers<S, A>(
    reducers: ReducerWithSideEffects<S, A>[]
  ): (
    state: S,
    action: A
  ) =>
    | typeof NO_UPDATE_SYMBOL
    | {
        state: S | undefined
        sideEffects: SideEffect<S, A>[]
      }
}

szg251 avatar May 11 '20 01:05 szg251

FYI, typings from @gege251 and @chrisdhanaraj have a minor difference from @conorhastings's implementation. In the UpdateWithSideEffect function, the sideEffects parameter may be EITHER a function OR an array of functions. This applies to the SideEffect function as well. You can see this in the mergeState function in the library (this line).

Here are updated types for UpdateWithSideEffect and SideEffect that handle both cases.

  export function SideEffect<ReducerState, ReducerActions>(
    args:
      | SideEffectCallback<ReducerState, ReducerActions>[]
      | SideEffectCallback<ReducerState, ReducerActions>
  ): SideEffectReturn<ReducerState, ReducerActions>

  export function UpdateWithSideEffect<ReducerState, ReducerActions>(
    state: any,
    sideEffects:
      | SideEffectCallback<ReducerState, ReducerActions>[]
      | SideEffectCallback<ReducerState, ReducerActions>
  ): UpdateWithSideEffectReturn<ReducerState, ReducerActions>

It's the same idea for @gege251's types but with the shorter generic parameters.

As an aside. Is there any problem with just adding a use-reducer-with-side-effects.d.ts file into the library without typescript support (as @gege251 had added in his PR) to make this easier for everyone using typescript? I'm fairly new to typescript so I didn't know if just the presence of the type definitions in the library was sufficient (it seems to be in the individual project). That seems like a far less obtrusive change that I'm assuming @conorhastings would be ok with pulling in since it literally doesn't change any existing code.

If anyone more familiar can chime in as to whether that's enough for adding typing then I'm happy to throw what I've got working from my project into a .d.ts file and make a PR.

stuckj avatar Apr 22 '21 21:04 stuckj

Made a PR for this here: https://github.com/conorhastings/use-reducer-with-side-effects/pull/45. I believe just using index.d.ts should work from what I read up about this on typescript. The alternative to putting this in the library is to submit it to DefinitelyTypes which should also work as long as you add @types/node to your project (if doing a node project).

stuckj avatar May 12 '21 20:05 stuckj