Typings
Opened this issue · 12 comments
Is there anyway we can get some typescript definitions for this library?
iirc @chrisdhanaraj has some he may be able to share
Yes! I'll open a PR tomorrow to definitely typed and cc you both
@chrisdhanaraj any chance I can get a copy of your types to add to my local type defs?
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.
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 Thank you! Will use those for now! 🙏
@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>];
}
Thank you @chrisdhanaraj!
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>[]
}
}
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.
Made a PR for this here: #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).