.derive(get, set): Optic
ts
Optic<A>.derive: <B>(selector: { get: (a: A) => B }) => Optic<B, readOnly>;Optic<A>.derive: <B>(lens: { get: (a: A) => B, set: (b: B, a: A) => A }) => Optic<B>;
ts
Optic<A>.derive: <B>(selector: { get: (a: A) => B }) => Optic<B, readOnly>;Optic<A>.derive: <B>(lens: { get: (a: A) => B, set: (b: B, a: A) => A }) => Optic<B>;
The derive
method allows you to derive a new optic from the current one with a custom get and set function.
- The
get
function is a simple selector, returning a new value derived from what the current optic is focused on. - The
set
function lets you specify how to update the original value when the derived one is updated. It is optional, if you don't pass it the method returns a read-only Optic.
tip
The get
function is memoized for you, it will only run when the original value changes.
Examples:
ts
constmillisecondsOptic =createState (15_000);constsecondsOptic =millisecondsOptic .derive ({get : (ms ) =>ms / 1000,set : (seconds ) =>seconds * 1000,});
ts
constmillisecondsOptic =createState (15_000);constsecondsOptic =millisecondsOptic .derive ({get : (ms ) =>ms / 1000,set : (seconds ) =>seconds * 1000,});
Here our secondsOptic
optic allows us to read and manipulate our time measurement in seconds even though it is represented in milliseconds in our state.
ts
secondsOptic .get (); // 15// make it on minutesecondsOptic .set (60);millisecondsOptic .get (); // 60_000
ts
secondsOptic .get (); // 15// make it on minutesecondsOptic .set (60);millisecondsOptic .get (); // 60_000
If you don't pass a set
function you get a read-only Optic:
ts
constsecondsOptic =millisecondsOptic .derive ({get : (ms ) =>ms / 1000 });
ts
constsecondsOptic =millisecondsOptic .derive ({get : (ms ) =>ms / 1000 });
- The derived type can be different from the original type:
ts
constobjectOptic =createState ({firstName : "Aaron",lastName : "Schwartz" });consttupleOptic =objectOptic .derive ({get : ({firstName ,lastName }) => [firstName ,lastName ] asconst ,set : ([firstName ,lastName ]) => ({firstName ,lastName }),});// The original optic is focused on an object while the derived optic is focused on a tuple
ts
constobjectOptic =createState ({firstName : "Aaron",lastName : "Schwartz" });consttupleOptic =objectOptic .derive ({get : ({firstName ,lastName }) => [firstName ,lastName ] asconst ,set : ([firstName ,lastName ]) => ({firstName ,lastName }),});// The original optic is focused on an object while the derived optic is focused on a tuple
- Here's how we would focus on an object's property with
derive
, if it wasn't already built-in:
ts
constpersonOptic =createState ({firstName : "Aaron",lastName : "Schwartz" });constlastNameOptic =personOptic .derive ({get : (person ) =>person .lastName ,set : (lastName ,person ) => ({ ...person ,lastName }),});
ts
constpersonOptic =createState ({firstName : "Aaron",lastName : "Schwartz" });constlastNameOptic =personOptic .derive ({get : (person ) =>person .lastName ,set : (lastName ,person ) => ({ ...person ,lastName }),});
info
Lawful optics
For your new optic to make sense the following laws must be respected.
Where a
is the original value and b
is the derived one:
get(set(b, a)) == b
(Ensures that when you set a value, you always get the same value back)set(get(a), a) == a
(Ensures that when you get a value and set it back, the original value doesn't change)