import Vue from 'vue' ; import Vuex from 'vuex' ; export type Reducer < S > = ( state : S , payload : any ) => S ; export interface ReducerTree < S > { [ key ] : Reducer < S > ; } export function reducers < S > ( reducers ) { return mapValues ( reducers , convertReducer ) ; } function convertReducer < S > ( reducer ) { return ( state , payload ) => { const prevState = shallowClone ( state ) ; const nextState = reducer ( prevState , payload ) ; if ( prevState === nextState ) return ; const res = compareState ( prevState , nextState ) ; res . set . forEach ( key => { Vue . set ( state , key , ( nextState as any ) [ key ] ) ; } ) ; res . delete . forEach ( key => { Vue . delete ( state , key ) ; } ) ; } ; } function compareState < S > ( prev , next ) { const scope = Object . keys ( prev ) ; const set = [ ] ; Object . keys ( next ) . forEach ( key => { if ( ( prev as any ) [ key ] !== ( next as any ) [ key ] ) { set . push ( key ) ; } const index = scope . indexOf ( key ) ; if ( index > - 1 ) { scope . splice ( index , 1 ) ; } } ) ; return { set , delete : scope } ; } function shallowClone < T > ( obj ) { return mapValues ( obj as any , v => v ) as any ; } function mapValues < T , R > ( obj , fn ) { const res = { } ; Object . keys ( obj ) . forEach ( key => { res [ key ] = fn ( obj [ key ] , key ) ; } ) ; return res ; }