Handling optional arguments and pruning them from the JS object
We can use [@bs.obj] to create a javascript object, and it correctly handles optional arguments. For example:
[@bs.obj]
external makeProps :
(
~a: int,
~b: string=?,
unit
) =>
_ =
"";
Js.log(makeProps(~a=10, ~b="Hi there", ()));
Js.log(makeProps(~a=10, ()));
Correctly prints out
{"a":10,"b":"Hi there"}
{"a":10}
However, if we want to type, or manipulate one of the parameters before calling makeProps then we lose the "optionalness" of the parameter:
type aType =
| One
| Two;
[@bs.obj] external makeProps : (~a: int, ~b: string=?, unit) => _ = "";
let theFn = (~a: int, ~b=?, ()) => {
let bStr =
switch b {
| None => ""
| Some(x) =>
switch x {
| One => "one"
| Two => "two"
}
};
let o = makeProps(~a, ~b=bStr, ());
o;
};
Js.log(theFn(~a=10, ~b=One, ()));
Js.log(theFn(~a=10, ()));
which prints:
{"a":10,"b":"one"}
{"a":10,"b":""}
Solution
We need to use a syntax "trick" to allow us to pass the option again (from https://reasonml.github.io/docs/en/function.html#explicitly-passed-optional). Specially, rather than converting the optional variant to a string, convert it to an optional string:
type aType =
| One
| Two;
[@bs.obj] external makeProps : (~a: int, ~b: string=?, unit) => _ = "";
let theFn = (~a: int, ~b=?, ()) => {
let bStr =
switch b {
| None => None
| Some(x) =>
switch x {
| One => Some("one")
| Two => Some("two")
}
};
let o = makeProps(~a, ~b=?bStr, ());
o;
};
Js.log(theFn(~a=10, ~b=One, ()));
Js.log(theFn(~a=10, ()));
which now returns:
{"a":10,"b":"one"}
{"a":10}
Refinement
Thanks to the excellent help from the https://discordapp.com/invite/reasonml we can reduce this further to:
type aType =
| One
| Two;
[@bs.obj] external makeProps : (~a: int, ~b: string=?, unit) => _ = "";
let theFn = (~a: int, ~b=?, ()) => {
let b =
Js.Option.map(
[@bs]
(
(x) =>
switch x {
| One => "one"
| Two => "two"
}
),
b
);
let o = makeProps(~a, ~b?, ());
o
};
Js.log(theFn(~a=10, ~b=One, ()));
Js.log(theFn(~a=10, ()));