Как определить привязку, которая принимает несколько типов в сигнатуре функции с помощью React-React?

При определении привязки «причина-реакция» я хочу знать, как я могу определить привязку, которая принимает несколько типов. Например, у меня есть аргумент ~value, который должен принимать: string, number, array(string) или array(number). На данный момент я использую option('a), но я не думаю, что это самый чистый подход, поскольку я бы предпочел явно определять тип. Как это может быть сделано? Я просмотрел bs.unwrap, но не знаю, как объединить внешний синтаксис в сигнатуру функции.

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
    );
};

В качестве побочного вопроса, поскольку числовой тип не определен, моя привязка также должна отображать числа с плавающей запятой и целое число?


person user465374    schedule 08.11.2017    source источник
comment
Оба float и int представлены как js number, так что технически вы можете обойтись простым использованием float, но вы, вероятно, захотите использовать оба для удобства.   -  person glennsl    schedule 08.11.2017


Ответы (2)


Это возможно с помощью следующего (на основе 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
    );
};

Это можно использовать следующим образом;

<Select value=(`IntArray([|10, 20|])) />
<Select value=(`Int(10)) />

Я скопировал toJsUnsafe из инструментария «причина-реакция», поэтому я не совсем уверен, что именно он делает. Я обновлю свой ответ, когда узнаю.

Функция unwrapValue принимает значение, которое может быть одним из перечисленных типов, и преобразует его в jsUnsafe.

Тип для unwrapValue допускает любой из перечисленных вариантов, но также допускает, например, их подмножество. (Это < перед вариантами, которые позволяют это сделать).

let option = (value: option([ | `String(string) | `Int(int)])) =>
  Js.Nullable.from_opt(option_map(unwrapValue, value));
person InsidersByte    schedule 08.11.2017
comment
"%identity" фактически является преобразованием типа. Он изменит тип переданного ему значения в соответствии с данной сигнатурой типа, не изменяя значение, и поэтому он также оптимизирован и, следовательно, не требует затрат времени выполнения. - person glennsl; 08.11.2017

Просто чтобы добавить к ответу @ InsidersByte, поскольку эта проблема не зависит от причины и может быть обобщена:

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"|])
];

Это решение также является автономным внутри модуля Value и не требует накладных расходов по сравнению с эквивалентом JavaScript, поскольку "%identity" внешние элементы - это не операции, которые оптимизируются.

person glennsl    schedule 08.11.2017