@@ -436,3 +436,97 @@ export const storageAvailable = (type: "localStorage" | "sessionStorage") => {
436
436
return false ;
437
437
}
438
438
} ;
439
+
440
+ // smaller implementation of lodash's isEqual from https://github.com/NickGard/tiny-isequal but made a bit more performant and typesafe
441
+ const toString = Object . prototype . toString ,
442
+ getPrototypeOf = Object . getPrototypeOf ,
443
+ getOwnProperties = Object . getOwnPropertySymbols
444
+ ? ( c : any ) => ( Object . keys ( c ) as any [ ] ) . concat ( Object . getOwnPropertySymbols ( c ) )
445
+ : Object . keys ;
446
+
447
+ const checkEquality = ( a : any , b : any , refs : any [ ] ) : boolean => {
448
+ // trivial case: primitives and referentially equal objects
449
+ if ( a === b ) return true ;
450
+
451
+ // if both are null/undefined, the above check would have returned true
452
+ if ( a == null || b == null ) return false ;
453
+
454
+ // check to see if we've seen this reference before; if yes, return true
455
+ // eslint-disable-next-line lodash/prefer-includes
456
+ if ( refs . indexOf ( a ) > - 1 && refs . indexOf ( b ) > - 1 ) return true ;
457
+
458
+ const aType = toString . call ( a ) ;
459
+ const bType = toString . call ( b ) ;
460
+
461
+ let aElements , bElements , element ;
462
+
463
+ // save results for circular checks
464
+ refs . push ( a , b ) ;
465
+
466
+ if ( aType != bType ) return false ; // not the same type of objects
467
+
468
+ // for non-null objects, check all custom properties
469
+ aElements = getOwnProperties ( a ) ;
470
+ bElements = getOwnProperties ( b ) ;
471
+ if (
472
+ aElements . length != bElements . length ||
473
+ aElements . some ( function ( key ) {
474
+ return ! checkEquality ( a [ key ] , b [ key ] , refs ) ;
475
+ } )
476
+ ) {
477
+ return false ;
478
+ }
479
+
480
+ switch ( aType . slice ( 8 , - 1 ) ) {
481
+ case "Symbol" :
482
+ return a . valueOf ( ) == b . valueOf ( ) ;
483
+ case "Date" :
484
+ case "Number" :
485
+ return + a == + b || ( + a != + a && + b != + b ) ; // convert Dates to ms, check for NaN
486
+ case "RegExp" :
487
+ case "Function" :
488
+ case "String" :
489
+ case "Boolean" :
490
+ return "" + a == "" + b ;
491
+ case "Set" :
492
+ case "Map" : {
493
+ aElements = a . entries ( ) ;
494
+ bElements = b . entries ( ) ;
495
+ do {
496
+ element = aElements . next ( ) ;
497
+ if ( ! checkEquality ( element . value , bElements . next ( ) . value , refs ) ) {
498
+ return false ;
499
+ }
500
+ } while ( ! element . done ) ;
501
+ return true ;
502
+ }
503
+ case "ArrayBuffer" :
504
+ ( a = new Uint8Array ( a ) ) , ( b = new Uint8Array ( b ) ) ; // fall through to be handled as an Array
505
+ case "DataView" :
506
+ ( a = new Uint8Array ( a . buffer ) ) , ( b = new Uint8Array ( b . buffer ) ) ; // fall through to be handled as an Array
507
+ case "Float32Array" :
508
+ case "Float64Array" :
509
+ case "Int8Array" :
510
+ case "Int16Array" :
511
+ case "Int32Array" :
512
+ case "Uint8Array" :
513
+ case "Uint16Array" :
514
+ case "Uint32Array" :
515
+ case "Uint8ClampedArray" :
516
+ case "Arguments" :
517
+ case "Array" :
518
+ if ( a . length != b . length ) return false ;
519
+ for ( element = 0 ; element < a . length ; element ++ ) {
520
+ if ( ! ( element in a ) && ! ( element in b ) ) continue ; // empty slots are equal
521
+ // either one slot is empty but not both OR the elements are not equal
522
+ if ( element in a != element in b || ! checkEquality ( a [ element ] , b [ element ] , refs ) ) return false ;
523
+ }
524
+ return true ;
525
+ case "Object" :
526
+ return checkEquality ( getPrototypeOf ( a ) , getPrototypeOf ( b ) , refs ) ;
527
+ default :
528
+ return false ;
529
+ }
530
+ } ;
531
+
532
+ export const isEqual = ( a : any , b : any ) => checkEquality ( a , b , [ ] ) ;
0 commit comments