function cloneSymbol ( targe ) { return Object ( Symbol . prototype . valueOf . call ( targe ) ) ; } function cloneReg ( targe ) { const reFlags = /\w*$/ ; const result = new targe . constructor ( targe . source , reFlags . exec ( targe ) ) ; result . lastIndex = targe . lastIndex ; return result ; } export const Types = { rawType ( value ) { return Object . prototype . toString . call ( value ) . slice ( 8 , - 1 ) ; } , isObject ( value ) { var type = typeof value ; return value != null && ( type === 'object' || type === 'function' ) ; } , isPlainObject ( value ) { if ( this . rawType ( value ) !== 'Object' ) { return false ; } const prototype = Object . getPrototypeOf ( value ) ; return prototype === null || prototype === Object . prototype ; } , isFunction ( value ) { return typeof value === 'function' ; } , isArray ( value ) { return Array . isArray ( value ) ; } , isEmpty ( value ) { return ( value === undefined || value === null || ( typeof value === 'string' && ! value . length ) || ( Types . isArray ( value ) && ! value . length ) || ( Types . isFunction ( value . isEmpty ) && value . isEmpty ( ) ) || ( Types . isArray ( value ) && ( value as any [ ] ) . every ( ( v ) => Types . isEmpty ( v ) ) ) || ( Types . isObject ( value ) && ! Object . keys ( value ) . filter ( ( k ) => value . hasOwnProperty ( k ) ) . length ) || ( Types . isPlainObject ( value ) && ! Object . keys ( value ) . filter ( ( k ) => value . hasOwnProperty ( k ) ) . length ) ) ; } , isEqual ( value1 , value2 ) { function getType ( obj ) { return Object . prototype . toString . call ( obj ) . slice ( 8 , - 1 ) . toLowerCase ( ) ; } function areDatesEqual ( ) { return value1 . getTime ( ) === value2 . getTime ( ) ; } function areArraysBufferEqual ( ) { if ( value1 . byteLength !== value2 . byteLength ) { return false ; } var view1 = new DataView ( value1 ) ; var view2 = new DataView ( value2 ) ; var i = value1 . byteLength ; while ( i -- ) { if ( view1 . getUint8 ( i ) !== view2 . getUint8 ( i ) ) { return false ; } } return true ; } function areArraysEqual ( ) { if ( value1 . length !== value2 . length ) return false ; for ( let i = 0 ; i < value1 . length ; i ++ ) { if ( ! Types . isEqual ( value1 [ i ] , value2 [ i ] ) ) return false ; } return true ; } function areObjectsEqual ( ) { if ( Object . keys ( value1 ) . length !== Object . keys ( value2 ) . length ) return false ; for ( let key in value1 ) { if ( Object . prototype . hasOwnProperty . call ( value1 , key ) ) { if ( ! Types . isEqual ( value1 [ key ] , value2 [ key ] ) ) return false ; } } return true ; } function areFunctionsEqual ( ) { return value1 . toString ( ) === value2 . toString ( ) ; } function arePrimativesEqual ( ) { return value1 === value2 ; } let type = getType ( value1 ) ; if ( type !== getType ( value2 ) ) return false ; if ( type === 'date' ) return areDatesEqual ( ) ; if ( type === 'arraybuffer' ) return areArraysBufferEqual ( ) ; if ( type === 'array' ) return areArraysEqual ( ) ; if ( type === 'object' ) return areObjectsEqual ( ) ; if ( type === 'function' ) return areFunctionsEqual ( ) ; return arePrimativesEqual ( ) ; } , clone ( target ) { const constrFun = target . constructor ; switch ( this . rawType ( target ) ) { case 'Boolean' : case 'Number' : case 'String' : case 'Error' : case 'Date' : return new constrFun ( target ) ; case 'RegExp' : return cloneReg ( target ) ; case 'Symbol' : return cloneSymbol ( target ) ; case 'Function' : return target ; default : return null ; } } , } ;