TelegramPWA/src/util/deepDiff.ts
2025-06-04 20:36:48 +02:00

81 lines
1.9 KiB
TypeScript

import { isLiteralObject, unique } from './iteratees';
const EQUAL = Symbol('EQUAL');
const DELETE = { __delete: true };
const DELETE_ALL_CHILDREN = { __deleteAllChildren: true };
type NestedDiff<T> = T extends object
? { [K in keyof T]?: NestedDiff<T[K]> | typeof DELETE_ALL_CHILDREN | typeof DELETE | null } : T;
export type DiffObject<T> = T extends object
? { [K in keyof T]?: NestedDiff<T[K]> } | typeof DELETE_ALL_CHILDREN
: T;
export function deepDiff<T>(value1: T, value2: T): Partial<T> | typeof EQUAL | typeof DELETE_ALL_CHILDREN {
if (value1 === value2) {
return EQUAL;
}
const type1 = typeof value1;
const type2 = typeof value2;
if (type1 !== type2) {
return value2;
}
if (Array.isArray(value1) && Array.isArray(value2) && areSortedArraysDeepEqual(value1, value2)) {
return EQUAL;
}
if (!isLiteralObject(value1) || !isLiteralObject(value2)) {
return value2;
}
const object1 = value1 as AnyLiteral;
const object2 = value2 as AnyLiteral;
const keys1 = Object.keys(object1);
const keys2 = Object.keys(object2);
if (!keys2.length) {
return !keys1.length ? EQUAL : DELETE_ALL_CHILDREN;
}
const allKeys = unique(keys1.concat(keys2));
const diff = allKeys.reduce((acc: any, key) => {
const subValue1 = object1[key];
const subValue2 = object2[key];
if (!object2.hasOwnProperty(key)) {
acc[key] = DELETE;
return acc;
}
if (!object1.hasOwnProperty(key)) {
acc[key] = subValue2;
return acc;
}
const subDiff = deepDiff(subValue1, subValue2);
if (subDiff !== EQUAL) {
acc[key] = subDiff;
}
return acc;
}, {});
if (Object.keys(diff).length === 0) {
return EQUAL;
}
return diff;
}
function areSortedArraysDeepEqual<T extends Array<any>>(array1: T, array2: T) {
if (array1.length !== array2.length) {
return false;
}
return array1.every((item, i) => deepDiff(item, array2[i]) === EQUAL);
}