01 · Problem
What is it for?
Global prototype extensions affect unrelated code and hide the direction of a comparison.
02 · Mental model
How should you think about it?
Return explicit left-to-right differences. A shallow change record contains the previous and next value for each changed own key.
Computes directional array differences and shallow own-key object changes without global mutation. Object values are compared by identity; nested structural comparison is deliberately outside the core.
03 · Control flow
What happens in which order?
- Collect candidate own keys or values.
- Compare membership or Object.is values.
- Record removed, added and changed entries.
- Return the result without mutating either input.
04 · Building blocks
The important contracts
arrayDifference(left, right)Return left values absent on the right.objectDifference(left, right)Return changed own keys as old/new value tuples.05 · Build it yourself
Implement a small core
Start with this small independent implementation, then use the steps below to make its contract explicit.
- Collect candidate own keys or values.
- Compare membership or Object.is values.
- Record removed, added and changed entries.
- Return the result without mutating either input.
function objectDiff(
left: Record<string, unknown>,
right: Record<string, unknown>,
) {
const keys = new Set([...Object.keys(left), ...Object.keys(right)]);
const changes = new Map<string, { before: unknown; after: unknown }>();
for (const key of keys) {
if (!Object.is(left[key], right[key])) {
changes.set(key, { before: left[key], after: right[key] });
}
}
return changes;
}06 · Verify
What should you test?
- Test added and removed keys including undefined.
- Test directional array differences.
- Verify imports leave global prototypes unchanged.
Try it yourself
Turn the first verification claim into a failing test, implement the smallest fix, then add a boundary case.
Test added and removed keys including undefined.
Compare your approach
Your check should fail when the stated invariant is deliberately broken, and pass again after the fix. Keep setup deterministic and assert an observable result.
07 · Limits
Trade-offs and failure modes
- Nested objects are compared by identity.
- Array difference retains duplicates from the left.
- Own-key presence and an undefined value are distinct.
Object values are compared by identity; nested structural comparison is deliberately outside the core.
08 · Keep exploring
Source code and related ideas
yarn tsx packages/complex-compare/examples/basic.ts