Handling Circular References Without Recursion Despite the many decent answers to this question, one was unable to find an iterative solution. Here's what I came up with:
Code:
type CircularReferenceHandler = (parentKey: IndexKey, parentValue: Dictionary&lt;IndexKey, any&gt;, childKey: IndexKey, childValue: Dictionary&lt;IndexKey, any&gt;) =&gt; void;
type Dictionary&lt;TKey extends IndexKey, TValue&gt; = Record&lt;TKey, TValue&gt;;
type IndexKey = (number | string);

const processCircularReferences = &lt;T extends object&gt;(input: T, handler: CircularReferenceHandler, initialPathValue = &quot;o&quot;): T =&gt; {
    const objectStack = [{ k: initialPathValue, v: input, },];
    const objectTracker = new WeakSet&lt;Dictionary&lt;IndexKey, any&gt;&gt;();

    objectTracker.add(input);

    while (objectStack.length) {
        const { k: parentKey, v: parentValue } = objectStack.shift()!;

        for (const [childKey, childValue] of Object.entries(parentValue)) {
            if ((childValue &amp;&amp; (&quot;object&quot; === typeof childValue))) {
                if (objectTracker.has(childValue)) {
                    handler(parentKey, parentValue, childKey, childValue);
                }
                else {
                    objectStack.push({ k: `${parentKey}.${childKey}`, v: childValue, });
                    objectTracker.add(childValue);
                }
            }
        }
    }

    return input;
}

Usage:
let o: any = { x: 0, y: 1, };

o.me = {};
o.me.self = o;
o.self = o;

processCircularReferences(o, (pK, pV, cK, _cV) =&gt; {
    console.log(`${pK}.${cK}`);

    pV[cK] = undefined;
});

console.log(o);

/*
    [log]: o.me.self
    [log]: o.self
    [log]: { 
               me: { 
                   self: undefined
               },
               self: undefined,
               x: 0,
               y: 1
           }
 */
Correctness
Following testcase breaks your code:
const o={};
o.x = o.y = {};

This is due to objectTracker tracks all seen values not all parent values.
Interface Design

initialPathValue looks strange to me. The parameter has no meaning to this function as far as I can tell.
Dot separated path won't work if any .s in keys. For example: { &quot;a.b&quot;: {}, &quot;a&quot;: { &quot;b&quot;: {} } }. To avoid any confusion, I would suggest use string[] instead.

Implementation

objectStack is named as Stack, but you are using it as a Queue. I believe there should be a mistake here. Maybe you want pop() instead of shift().
parentValue is a WeakSet. But I cannot find a reason why any objects in it will be garbage collected during the functions life cycle. So maybe a simple Set would fint your requirement.