I'm a little lost in the documentation of JS.Exn

This is an example from the “Language Manual”

try {
  someJSFunctionThatThrows()
} catch {
| Js.Exn.Error(obj) =>
  switch Js.Exn.message(obj) {
  | Some(m) => Js.log("Caught a JS exception! Message: " ++ m)
  | None => ()
  }
}

It goes onto explain

To distinguish between JavaScript exceptions and ReScript exceptions, ReScript namespaces JS exceptions under the Js.Exn.Error(payload) variant.

That’s the “Manual”, but I often like to check the docs. But where is it? I can’t find it anywhere. Shouldn’t a Js.Exn variant be listed in the Js.Exn section? It seems important. Is there a meaningful reason I can’t find it, or was it just never documented? The weird search doesn’t have it either.

1 Like

Ok maybe I found it here

type exn +=
  private
  | Error(t)

But more like a riddle than documentation.

For anybody who is lost like me, this:

type exn +=
  private
  | Error(t)

is a Extensible Variant

Extensible Variant | ReScript Language Manual, which is what is used for exceptions.

You can add to the pool of possible exceptions with

exception InputClosed(string)

Then later

try {
  maybeRaiseSomething()
  raise(InputClosed("The stream has closed!"))
} catch {
| InputClosed(err) => Js.log2("InputClosed", err)
| Js.Exn.Error(err) => Js.log2("Js.Exn.Error", err)
}

I still find it confusing that there’s no way from the documentation to tell where something of type exn comes from.

I’m also still unclear on whether type exn is in the Js module (and “try/catch” catches all kinds of exn) or if its some global exn thats “appened” to because its extensible and catch only needs to catch the global exn.

Exceptions are scoped to their modules (like most things in ReScript). So the Js.Exn.Error is a constructor named Error that’s defined in the Js.Exn module. There are a few built-in exceptions (Failure, Not_found) that are always in scope.

The exn type is a built-in ReScript type (like string, option, etc). The Js.Exn.Error exception is just one constructor that’s added to it. try/catch will match anything of the exn type.

1 Like

Is the reason that Js.Exn.Error wont tab-complete is because its a extensible variant? Not finding it in the list make this extra confusing.

Also, is there anyway to get anything out of it besides name() and message()? I’m dealing with node errors, and name is always “Error”, message is useless for matching, and “code” is the real info. Seems like an oversight.

I have no idea. That would be a question for whichever editor extension you’re using.

As per the API docs, there are also built-in functions to get the stack and filename. AFAIK, code is a Node-specific error property. You can add your own binding for it though:

@get external code: Js.Exn.t => string = "code"

The rescript-nodejs package also has bindings for it. (Along with lots of other Node bindings.)