Equality helpers need a precise contract before they need recursion. This lesson compares plain objects by own keys, arrays by ordered elements, Dates by their represented instant, and primitive values with Object.is. Under that last rule NaN equals itself, while positive zero and negative zero differ.
Object key order is not meaningful, so serialized strings are not a reliable comparison. Counting keys is also insufficient: two records can have the same number of different keys. You will test those distinctions independently. The implementation tracks object pairs so repeated visits terminate, but the public tasks stay within the stated data model. Maps, Sets, class instances, symbols, and arbitrary property descriptors need additional rules and are outside these equality exercises. Prefer an honest limited contract to an ambiguous universal claim.
function deepEqual(a:unknown,b:unknown,pairs=new WeakMap<object,object>()):boolean {
if(Object.is(a,b))return true;
if(a===null || b===null || typeof a!=="object" || typeof b!=="object")return false;
if(Array.isArray(a)!==Array.isArray(b))return false;
if(a instanceof Date || b instanceof Date)return a instanceof Date && b instanceof Date && a.getTime()===b.getTime();
if(pairs.has(a))return pairs.get(a)===b;
pairs.set(a,b);const ak=Object.keys(a),bk=Object.keys(b);if(ak.length!==bk.length)return false;
return ak.every(key=>Object.prototype.hasOwnProperty.call(b,key) && deepEqual((a as Record<string,unknown>)[key],(b as Record<string,unknown>)[key],pairs));
}
function solve(value:string):boolean {const x=JSON.parse(value) as {a:unknown;b:unknown};return deepEqual(x.a,x.b);}
A complete implementation for the first scenario; trace its input before editing.