Function mirrorObjectThroughComposition

create a new object that mirrors that functionality of an existing object obj, through composition.

import { assertEquals } from "jsr:@std/assert"

type TypeofB = Array<number> & { getLength(): number }
type TypeofC = TypeofB & { countZeros(): number }

const a = Array.prototype
const b = {
getLength(): number { return this.length }
} as TypeofB
const c = {
countZeros(): number { return [...this].filter((value) => (value === 0)).length }
} as TypeofC

Object.setPrototypeOf(b, a)
Object.setPrototypeOf(c, b)

// below, we create an object `d` that mirrors the methods and protperties of `c`, but does not actually inherit it.
const d = mirrorObjectThroughComposition(c, {
baseKey: "_super",
propertyKeys: ["length"],
})

// notice that `d` does not inherit `c` as its prototype, despite being able to utilize its methods and properties.
assertEquals(Object.getPrototypeOf(d), Object.prototype, "`d` does not inherit `c`")

d.push(0, 0, 0, 0, 1)
assertEquals([...d], [0, 0, 0, 0, 1], "`d` also mirrors symbolic keys (such as iterators)")
assertEquals([...c], [0, 0, 0, 0, 1], "mutations made to `d` are applied to `c`")
assertEquals(d._super, c, "`c` is accessible via the `baseKey` (\"_super\") property")
assertEquals(d.length, 5)
assertEquals(c.length, 5)
assertEquals(d.getLength(), 5)
assertEquals(d.countZeros(), 4)
d.splice(0, 3)
assertEquals(d.countZeros(), 1)
assertEquals(c.countZeros(), 1)

// you may even hot-swap the object that is being composed inside of `d`.
const e = [1, 2, 3, 4, 5, 6, 7, 8, 9]
Object.setPrototypeOf(e, c)
d._super = e as TypeofC
assertEquals(d.length, 9)
assertEquals(d.getLength(), 9)
assertEquals(d.countZeros(), 0)

// it is also possible for you to provide an existing `target` object onto which the mirroring should occur.
// moreover, you can ignore specific keys which you would like not to be mirrored using the `ignoreKeys` option.
class F {
methodF() { return "press f for your fallen comrades" }
// to stop typescript from complaining, we declare the instance methods and properties that will be mirrored.
declare _super: typeof c
declare length: number
declare getLength: undefined
declare countZeros: () => number
}
mirrorObjectThroughComposition(c, {
target: F.prototype,
baseKey: "_super",
propertyKeys: ["length"],
ignoreKeys: ["getLength"],
})
const f = new F()
assertEquals(f instanceof F, true)
assertEquals(f._super, c)
assertEquals(f.length, 2)
assertEquals(f.countZeros(), 1)
assertEquals(f.getLength, undefined) // the `getLength` is not mirrored because it was present in the `ignoreKeys` option
assertEquals(f.methodF(), "press f for your fallen comrades")