Type error with simple code

Hi,

I can’t seem to figure out why I don’t get a type error at the 4th line and instead get a type error at the 12th line.

type history = { "push": string => unit }
@bs.module("@core") external browserHistory: history = "browserHistory"

Js.log(browserHistory["push"]) // Line 4: No type error

type clickHandler = (ReactEvent.Mouse.t) => unit
type makeClickHandler = (string) => clickHandler

let makeDefaultClickHandler: makeClickHandler = (href) => {
    let pushHistoryClickHandler: clickHandler = (evt) => {
        ReactEvent.Mouse.preventDefault(evt)
        browserHistory["push"](href) // Line 12: Type error
    }

    pushHistoryClickHandler
}

@genType
@react.component
let make = (~children: React.element, ~href: string, ~onClick: option<clickHandler>=?) => {
    let onClick = switch onClick {
        | Some(onClick) => onClick
        | None => makeDefaultClickHandler(href)
    }

    <a href onClick> children </a>
}
1 Like

Can you provide more details? What is the error message?

Here is a screenshot for you:

Annotation 2021-01-28 031050

Going forward, for accessibility reasons, please post text instead of screenshots.

In this case, the error comes from the fact that functions and methods are different and have different types in ReScript. You will need to do a binding in a specific way to use method calls. Check https://rescript-lang.org/docs/manual/latest/bind-to-js-function#object-method

As to your previous question it did not error at the 4th line because at that point you had not tried to call the push property; you were just printing it, and Js.log prints anything you give it–it doesn’t care about the type.

1 Like

Thanks for the response.

For accessibility sake, here is the error:

This has type: 'a => unit
  Somewhere wanted: Js_OO.Meth.arity1<'b>

Unfortunately the example in the doc isn’t incredibly useful as I need to bind to the method belonging to an object returned by ES6 module, not global document object. I did get it to work though:

type history
@bs.send external push: (history, string) => unit = "push"
@bs.module("@core") external browserHistory: history = "browserHistory"
...
push(browserHistory, "https://example.com")

You mentioned methods and functions having different types in ReScript. I couldn’t find any mention of methods in the docs, how are they supported? The following doesn’t seem to work:

let browserHistory = { "push": href => Js.log(href) }
browserHistory["push"]("https://example.com")

It looks like you are using a JS object type and you want to use its function as a method, so I guess you need to use the @meth decorator on the right attribute to mark it as a method.

Example:

type history = { @meth "push": string => unit }
@bs.module("@core") external browserHistory: history = "browserHistory"

browserHistory["push"]("test") 

Playground Link

See https://rescript-lang.org/syntax-lookup#meth-decorator in the syntax lookup widget.

1 Like

Thank you, that’s much better.

So I suppose there is no @meth equivalent in pure ReScript right?

The way you bound it is almost exactly how I would have done it, with @bs.module. The only doubt in my mind is, is @core a valid module? Shouldn’t it be @core/something?

I can definitely see merit for both approaches, it comes down to personal preference I guess.

As for the @core module, it’s just an alias mapped to another directory under the same source tree in order to make my life a bit easier (babel-plugin-module-resolver). Just need to be careful not to map to any actual npm modules, eh :slight_smile:

Still, why is everyone dodging my methods-in-pure-rescript question? :stuck_out_tongue:

Not sure what you mean by “methods in pure ReScript”. The recommended way doing OOP is using @send and ->, which is essentially doing the same thing, but with less extra concepts.

3 Likes

It’s not a dodge. As @ryyppy said–in ReScript the idiomatic way is to bind to JavaScript classes and methods using ReScript abstract types and functions. See https://github.com/yawaramin/bucklescript-bindings-cookbook#classes-and-oop (ReasonML syntax but same concepts) for more.

2 Likes

Thank you for the clarification, that’s exactly what I needed to know. It’s definitely a paradigm shift for me as I have been so accustomed to passing around functions in structured objects. Marking it as solved now.

1 Like