.derive(get, set): Optic
ts
Optic<A>.derive: <B>(get: (a: A) => B) => ReadOptic<B>;Optic<A>.derive: <B>(get: (a: A) => B, set: (b: B, a: A) => A) => Optic<B>;
ts
Optic<A>.derive: <B>(get: (a: A) => B) => ReadOptic<B>;Optic<A>.derive: <B>(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.
All the built-in methods in this library can be implemented using derive
.
- 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 to 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 aReadOptic
.
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 ((ms ) =>ms / 1000,(seconds ) =>seconds * 1000);
ts
constmillisecondsOptic =createState (15_000);constsecondsOptic =millisecondsOptic .derive ((ms ) =>ms / 1000,(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 ReadOptic
, an optic that can't be updated:
ts
constsecondsOptic =millisecondsOptic .derive ((ms ) =>ms / 1000);
ts
constsecondsOptic =millisecondsOptic .derive ((ms ) =>ms / 1000);
- The derived type can be different from the original type:
ts
constobjectOptic =createState ({firstName : "Aaron",lastName : "Schwartz" });consttupleOptic =objectOptic .derive (({firstName ,lastName }) => [firstName ,lastName ] asconst ,([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 (({firstName ,lastName }) => [firstName ,lastName ] asconst ,([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 ((person ) =>person .lastName ,(lastName ,person ) => ({ ...person ,lastName }));
ts
constpersonOptic =createState ({firstName : "Aaron",lastName : "Schwartz" });constlastNameOptic =personOptic .derive ((person ) =>person .lastName ,(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)