How to write reasonml binding for a union type
Asked Answered
O

1

5

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)) = "";
Ordonez answered 12/9, 2018 at 14:5 Comment(0)
C
8

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.

Capsize answered 12/9, 2018 at 15:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.