import React from 'react' ; export type Model < State , Events extends IEvent > = { state : State ; events : Events ; } ; type IEvent = { [ key ] : { bivarianceHack ( data ? ) : void ; } [ 'bivarianceHack' ] ; } ; template TEvents template TGuardMap template TCallbackMap type EventMap < TEvents extends IEvent , TGuardMap extends Record < string , keyof TEvents > , TCallbackMap extends Record < string , keyof TEvents > > = { guards : TGuardMap ; callbacks : TCallbackMap ; } ; type ToGuardConfig < TState extends Record < string , any > , TEvents extends IEvent , TGuardMap extends Record < string , keyof TEvents > > = { [ K in keyof TGuardMap ] : ( event : { data : Parameters < TEvents [ TGuardMap [ K ] ] > [ 0 ] ; state : TState ; } ) => boolean ; } ; type ToCallbackConfig < TState extends Record < string , any > , TEvents extends IEvent , TCallbackMap extends Record < string , keyof TEvents > > = { [ K in keyof TCallbackMap ] : ( event : { data : Parameters < TEvents [ TCallbackMap [ K ] ] > [ 0 ] ; /**
         * Callbacks are called during the `setState` phase in React. This means the state has not
         * resolved yet. This is a good time to add more `setState` calls which will be added to React's
         * state batch updates, but it also means the state provided here hasn't been updated yet.
         */ prevState : TState ; } ) => void ; } ; example export type ToModelConfig < TState extends Record < string , any > , TEvents extends IEvent , TEventMap extends EventMap < TEvents , any , any > > = ToGuardConfig < TState , TEvents , TEventMap [ 'guards' ] > & ToCallbackConfig < TState , TEvents , TEventMap [ 'callbacks' ] > ; example export const createEventMap = < TEvents extends IEvent > ( ) => < TGuardMap extends Record < string , keyof TEvents > , TCallbackMap extends Record < string , keyof TEvents > > ( config ) : EventMap < TEvents , TGuardMap , TCallbackMap > => { return config as EventMap < TEvents , TGuardMap , TCallbackMap > ; } ; const keys = < T extends object > ( input ) => Object . keys ( input ) as ( keyof T ) [ ] ; param eventMap param state param config param events example export const useEventMap = < TEvents extends IEvent , TState extends Record < string , any > , TGuardMap extends Record < string , keyof TEvents > , TCallbackMap extends Record < string , keyof TEvents > , TConfig extends Partial < ToModelConfig < TState , TEvents , EventMap < TEvents , TGuardMap , TCallbackMap > > > > ( eventMap , state , config , events ) : TEvents => { const eventMapRef = React . useRef ( eventMap ) ; const stateRef = React . useRef ( state ) ; const configRef = React . useRef ( config ) ; const eventsRef = React . useRef ( events ) ; eventMapRef . current = eventMap ; stateRef . current = state ; configRef . current = config ; eventsRef . current = events ; return React . useMemo ( ( ) => { return keys ( eventsRef . current ) . reduce ( ( result , key ) => { result [ key ] = ( data => { if ( ! ( eventsRef . current [ key ] as any ) . _wrapped ) { const guardFn = keys ( eventMapRef . current . guards || { } ) . find ( k => { return ( eventMapRef . current . guards || { } ) [ k ] === key ; } ) ; if ( guardFn && configRef . current ?. [ guardFn ] && ! configRef . current [ guardFn ] ( { data , state : stateRef . current } ) ) { return ; } } eventsRef . current [ key ] ( data ) ; if ( ! ( eventsRef . current [ key ] as any ) . _wrapped ) { const callbackFn = keys ( eventMapRef . current . callbacks || { } ) . find ( k => { return ( eventMapRef . current . callbacks || { } ) [ k ] === key ; } ) ; if ( callbackFn && configRef . current ?. [ callbackFn ] ) { configRef . current [ callbackFn ] ( { data , prevState : stateRef . current } ) ; } } } ) as TEvents [ keyof TEvents ] ; ( result [ key ] as any ) . _wrapped = true ; return result ; } , { } as TEvents ) ; } , [ ] ) ; } ; type EventCreator = { [ key ] : { bivarianceHack ( ... args ) : object ; } [ 'bivarianceHack' ] ; } ; type ToEvent < TEvents extends EventCreator > = { [ K in keyof TEvents ] : ( data : ReturnType < TEvents [ K ] > ) => void ; } ; export const createEvents = < TEvents extends EventCreator > ( events ) => < TGuardMap extends Record < string , keyof TEvents > , TCallbackMap extends Record < string , keyof TEvents > > ( config ? ) => { return { events , eventMap : config as EventMap < ToEvent < TEvents > , TGuardMap , TCallbackMap > } ; } ;