Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write reasonml binding for a union type

I am trying to write bindings for https://github.com/oblador/react-native-keychain/blob/master/typings/react-native-keychain.d.ts#L76

getGenericPassword returns false if an error, else an object (credentials). I am not sure this union type can be represented in reason, but a better API would be the result an option (option(credentials)). But, how can I convert Promise<boolean | credentials> -> Js.Promise.t(option(credentials)) in the binding file. Below is a template.

Thanks for your help.

[@bs.deriving abstract]
type credentials = {
  service: string,
  username: string,
  password: string,
};

/* TODO convert the actual return value 
Js.Promise.t(option(credentials)) to more reason type 
Js.Promise.t(option(credentials)) */

[@bs.module "react-native-keychain"] [@bs.scope "default"]
external getGenericPassword: unit => Js.Promise.t(option(credentials)) = "";
like image 612
bsr Avatar asked Sep 12 '18 14:09

bsr


1 Answers

You can use Js.Types.classify to get the runtime type of a value.

type maybeCredentials;

[@bs.module "react-native-keychain"] [@bs.scope "default"]
external getGenericPassword: unit => Js.Promise.t(maybeCredentials) = "";

let getGenericPassword: unit => Js.Promise.t(option(credentials)) =
  () =>
    Js.Promise.(
      getGenericPassword()
      |> then_(maybeCredentials =>
           switch (Js.Types.classify(maybeCredentials)) {
           | JSObject(obj) => resolve(Some(obj |> Obj.magic))
           | _ => resolve(None)
           }
         )
    );

Here maybeCredentials is defined and used as an intermediate type.

We then define a function with the same name as the binding, which will "shadow" the name and prevent the binding from being used directly in favour of our "override". However, within the override we're still able to use the binding.

We then call Js.Types.classify to get the runtime type of the returned value. If it is an object we use Obj.magic to cast the abstract obj_type to our credentials type (inferred from the return type of the function), and wrap it in an option. For any other type we return None.

By the way, this kind of "type" is called an untagged union. I've written down a few examples using different strategies for dealing with these, as both a producer and a consumer, in bucklescript-cookbook.

like image 139
glennsl Avatar answered Oct 19 '22 13:10

glennsl