When defining a reason-react binding and I want to know how I can determine a binding that accepts multiple types. For example, I have an argument ~value
that should accept: string
, number
, array(string)
or array(number)
. At the moment I am using option('a)
but I do not think this is the cleanest approach as I would prefer to define the type explicitly. How can this be done? I have looked at bs.unwrap
but I am unsure how to combine external syntax into a function signature.
module Select = {
[@bs.module "material-ui/Select"] external reactClass : ReasonReact.reactClass = "default";
let make =
(
...
~menuProps: option(Js.t({..}))=?,
~value: option('a), /* Should be type to string, number, Array of string and Array of number */
~style: option(ReactDOMRe.style)=?,
...
children
) =>
ReasonReact.wrapJsForReason(
~reactClass,
~props=
Js.Nullable.(
{
...
"value": from_opt(value),
"style": from_opt(style)
}
),
children
);
};
As a side question, as number type is not defined in reason would my binding also have to map float and integer into numbers?
This is possible by using the following (inspired by https://github.com/astrada/reason-react-toolbox/).
type jsUnsafe;
external toJsUnsafe : 'a => jsUnsafe = "%identity";
let unwrapValue =
(r: [< | `Int(int) | `IntArray(array(int)) | `String(string) | `StringArray(array(string))]) =>
switch r {
| `String(s) => toJsUnsafe(s)
| `Int(i) => toJsUnsafe(i)
| `StringArray(a) => toJsUnsafe(a)
| `IntArray(a) => toJsUnsafe(a)
};
let optionMap = (fn, option) =>
switch option {
| Some(value) => Some(fn(value))
| None => None
};
module Select = {
[@bs.module "material-ui/Select"] external reactClass : ReasonReact.reactClass = "default";
let make =
(
...
~menuProps: option(Js.t({..}))=?,
~value:
option(
[ | `Int(int) | `IntArray(array(int)) | `String(string) | `StringArray(array(string))]
)=?,
~style: option(ReactDOMRe.style)=?,
...
children
) =>
ReasonReact.wrapJsForReason(
~reactClass,
~props=
Js.Nullable.(
{
...
"value": from_opt(optionMap(unwrapValue, value)),
"style": from_opt(style)
}
),
children
);
};
This can be used in the following way;
<Select value=(`IntArray([|10, 20|])) />
<Select value=(`Int(10)) />
I copied toJsUnsafe
from reason-react-toolbox, so I'm not entirely sure exactly what it does, I will update my answer when I find out.
The unwrapValue
function takes a value which can be one of the types listed and converts it to jsUnsafe.
The type for unwrapValue
allows for any of variants listed, but also allows a subset of those, for example. (It's the <
before the variants that enable this).
let option = (value: option([ | `String(string) | `Int(int)])) =>
Js.Nullable.from_opt(option_map(unwrapValue, value));
Just to add to @InsidersByte's answer, since this problem isn't reason-react-specific and can be generalized:
module Value = {
type t;
external int : int => t = "%identity";
external intArray : array(int) => t = "%identity";
external string : string => t = "%identity";
external stringArray : array(string) => t = "%identity";
};
let values : list(Value.t) = [
Value.int(4),
Value.stringArray([|"foo", "bar"|])
];
This solution is also self-contained inside the Value
module, and incurs no overhead compared to the JavaScript equivalent since "%identity"
externals are no-ops that are optimized away.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With