-
Notifications
You must be signed in to change notification settings - Fork 11
Description
Support nesting regular objects inside the returned value from lens.reflect
.
Although not necessarily in-scope for lenses, it may be possible to support both lenses and nested objects with lenses from the reflect
method by automatically applying the reflect
method for anything that isn't a lens instance whenever using focus
.
Example
const reflected = lens.reflect((_dictionary, lens) => {
a: lens.focus('parent.a'),
b: {
c: {
d: lens.focus('parent.b.c.d')
}
}
})
Details
After #43 was implemented, nested lenses were supported properly. This requires that all values in the object are also lenses.
const reflected = lens.reflect((dictionary, lens) => {
a: dictionary.a,
b: lens.focus('b'),
c: lens.reflect(() => {
return {
d: lens.reflect(() => {
e: lens.focus(...)
})
}
})
})
I'm fine with this and would appreciate this being documented. However, I happened to be nesting my lenses like this.
I was using JS and the type errors weren't detected. The code just happened to look like it was working fine. So it is actually documented by TS and you'll receive errors for doing this. My implementation with updated typings seems to allow this pattern and generate correct path names.
const reflected = lens.reflect((dictionary, lens) => {
a: dictionary.a,
b: lens.focus('b'),
c: {
d: {
e: lens.focus('a.b.c.d.e')
}
}
})
Here, c
is not a lens, but contains a deeply nested lens. This is significant because this line of code will error because reflected.focus('c')
will return the object { d: { e } }
instead of another LensCore
object.
This differs from the previous implementation that would previously resolve the entire path at once and always return a LensCore
object. While it may be out of scope to support nesting anything besides lens, here is a potential implementation to support both strategies.
Implementation
Original source location
Lines 68 to 74 in e442fe6
const overriddenLens: LensCore<T> | undefined = get(this.override, propString); | |
if (!overriddenLens) { | |
const result = new LensCore(this.control, nestedPath, this.cache); | |
this.cache?.set(result, nestedPath); | |
return result; | |
} |
Possible solution
let overriddenLens: LensCore<T> | Undefined
// The idea is that values in the reflected object may be nested objects or simply lenses at any level.
type NestedLens = Record<string, LensCore<T> | NestedLens>
const overriddenLensOrRecord: LensCore<T> | NestedLens | undefined = get(this.override, propString);
// use this.constructor in case this method is being called from a super class?
if (overriddenLensOrRecord instanceof this.constructor) {
overriddenLens = overriddenLensOrRecord
} else if (overriddenLensOrRecord) {
overriddenLens = this.reflect(() => overriddenLensOrRecord)
} else {
const result = new LensCore(this.control, nestedPath, this.cache);
this.cache?.set(result, nestedPath);
return result;
}