Skip to content

Commit

Permalink
feat: type-safe _deepEquals
Browse files Browse the repository at this point in the history
  • Loading branch information
kirillgroshkov committed Aug 2, 2024
1 parent 1abdef2 commit 303a8cc
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 16 deletions.
2 changes: 1 addition & 1 deletion src/object/deepEquals.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,6 @@ class A {
}

test('debug', () => {
const r = _deepEquals(new A('a'), 'a')
const r = _deepEquals(new A('a'), 'a' as any)
console.log(r)
})
32 changes: 17 additions & 15 deletions src/object/deepEquals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
TLDR: _deepEquals should be useful in most of the cases, start there.
*/
export function _deepEquals(a: any, b: any): boolean {
export function _deepEquals<T>(a: T, b: T): boolean {
if (a === b) return true

if (Number.isNaN(a)) {
Expand All @@ -52,7 +52,7 @@ export function _deepEquals(a: any, b: any): boolean {

if (Array.isArray(a)) {
const length = a.length
if (length !== b.length) return false
if (!Array.isArray(b) || length !== b.length) return false
for (let i = length; i-- !== 0; ) {
if (!_deepEquals(a[i], b[i])) return false
}
Expand All @@ -74,12 +74,14 @@ export function _deepEquals(a: any, b: any): boolean {
return true
}

if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags
if (a.constructor === RegExp) {
return (a as RegExp).source === (b as any).source && (a as RegExp).flags === (b as any).flags
}
if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf()
if (a.toString !== Object.prototype.toString) return a.toString() === b.toString()

for (const key of new Set([...Object.keys(a), ...Object.keys(b)])) {
if (!_deepEquals(a[key], b[key])) return false
if (!_deepEquals(a[key as keyof T], b[key as keyof T])) return false
}

return true
Expand All @@ -102,44 +104,44 @@ export function _deepEquals(a: any, b: any): boolean {
See _deepEquals docs for more details and comparison.
*/
export function _deepJsonEquals(a: any, b: any): boolean {
export function _deepJsonEquals<T>(a: T, b: T): boolean {
if (a === b) return true

if (Number.isNaN(a)) {
a = null
;(a as any) = null
} else if (typeof a === 'function') {
a = undefined
;(a as any) = undefined
} else if (a && typeof a === 'object') {
if (a instanceof Date) {
a = a.valueOf()
;(a as any) = a.valueOf()
} else if ('toJSON' in a) {
a = a.toJSON()
a = (a as any).toJSON()
}
}
if (Number.isNaN(b)) {
b = null
;(b as any) = null
} else if (typeof b === 'function') {
b = undefined
;(b as any) = undefined
} else if (b && typeof b === 'object') {
if (b instanceof Date) {
b = b.valueOf()
;(b as any) = b.valueOf()
} else if ('toJSON' in b) {
b = b.toJSON()
b = (b as any).toJSON()
}
}

if (a && b && typeof a === 'object' && typeof b === 'object') {
if (Array.isArray(a)) {
const length = a.length
if (length !== b.length) return false
if (!Array.isArray(b) || length !== b.length) return false
for (let i = length; i-- !== 0; ) {
if (!_deepJsonEquals(a[i], b[i])) return false
}
return true
}

for (const key of new Set([...Object.keys(a), ...Object.keys(b)])) {
if (!_deepJsonEquals(a[key], b[key])) return false
if (!_deepJsonEquals(a[key as keyof T], b[key as keyof T])) return false
}

return true
Expand Down

0 comments on commit 303a8cc

Please sign in to comment.