2017-11-08 13 views
2

Bei der Definition einer Ursache-Reaktion-Bindung und ich möchte wissen, wie ich eine Bindung ermitteln kann, die mehrere Typen akzeptiert. Zum Beispiel habe ich ein Argument ~value, das akzeptieren sollte: string, number, array(string) oder array(number). Im Moment verwende ich option('a), aber ich denke nicht, dass dies der sauberste Ansatz ist, da ich den Typ lieber explizit definieren würde. Wie kann das gemacht werden? Ich habe bs.unwrap betrachtet, aber ich bin unsicher, wie man externe Syntax in eine Funktionssignatur kombiniert.Wie definiert man eine Bindung, die mehrere Typen in der Funktionssignatur mit reason-react akzeptiert?

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

Als Seite Frage, wie Nummerntyp nicht float und integer in Zahlen definiert ist, müssen in Grund würde meine Bindung auch Routenkarte?

+0

Sowohl 'float' als auch' int' werden als js 'number' dargestellt, so dass Sie technisch mit" float "umgehen können, aber Sie werden wahrscheinlich beide aus Bequemlichkeit verwenden wollen. – glennsl

Antwort

2

Dies ist möglich mit dem folgenden (inspiriert von 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 
    ); 
}; 

Dies kann auf folgende Weise verwendet werden;

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

Ich kopierte toJsUnsafe von Grunde reagieren-Toolbox, so dass ich bin mir nicht ganz sicher, was es tut, werde ich meine Antwort aktualisieren, wenn ich herausfinden.

Die Funktion unwrapValue nimmt einen Wert, der einer der aufgelisteten Typen sein kann, und konvertiert ihn in jsUnsafe.

Der Typ für unwrapValue ermöglicht für jede der aufgelisteten Varianten, sondern ermöglicht auch eine Teilmenge von denen, zum Beispiel. (Es ist die < vor den Varianten, die dies ermöglichen).

let option = (value: option([ | `String(string) | `Int(int)])) => 
    Js.Nullable.from_opt(option_map(unwrapValue, value)); 
+0

'"% identity "' ist effektiv ein Typ-Cast. Es ändert den Typ des übergebenen Werts entsprechend der angegebenen Typ-Signatur, ohne den Wert zu ändern. Daher wird es auch weg optimiert und verursacht daher keine Laufzeitkosten. – glennsl

3

Gerade @ InsidersByte Antwort hinzuzufügen, da dieses Problem nicht Grund reagiert spezifisch ist und verallgemeinert werden kann:

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

Diese Lösung auch im Innern des Value Modul selbst enthalten ist, und es entsteht kein Overhead im Vergleich zum JavaScript-Äquivalent, da "%identity" Externals No-Ops sind, die wegoptimiert sind.

Verwandte Themen