Docs / Language Manual / External (Bind to Any JS Library)
Edit

External (Bind to Any JS Library)

external is the primary ReScript features for bringing in and using JavaScript values.

external is like a let binding, but:

  • The right side of = isn't a value; it's the name of the JS value you're referring to.

  • The type for the binding is mandatory, since we need to know what the type of that JS value is.

  • Can only exist at the top level of a file or module.

ReScriptJS Output
@val external setTimeout: (unit => unit, int) => float = "setTimeout"

There are several kinds of externals, differentiated and/or augmented through the attribute they carry. This page deals with the general, shared mechanism behind most externals. The different are documented in their respective pages later. A few notable ones:

You can also use our Syntax Lookup tool to find them.

Related: see also our list of external decorators.

Usage

Once declared, you can use an external as a normal value, just like a let binding.

Tips & Tricks

external + ReScript objects are a wonderful combination for quick prototyping. Check the JS output tab:

ReScriptJS Output
// The type of document is just some random type 'a
// that we won't bother to specify
@val external document: 'a = "document"

// call a method
document["addEventListener"]("mouseup", _event => {
  Js.log("clicked!")
})

// get a property
let loc = document["location"]

// set a property
document["location"]["href"] = "rescript-lang.org"

We've specified document's type as 'a, aka a placeholder type that's polymorphic. Any value can be passed there, so you're not getting much type safety (except the inferences at various call sites). However, this is excellent for quickly getting started using a JavaScript library in ReScript without needing the equivalent of a repository of typed bindings like TypeScript's DefinitelyTyped repo.

However, if you want to more rigidly bind to the JavaScript library you want, keep reading the next few interop pages.

Performance & Output Readability

externals declarations are inlined into their callers during compilation, and completely disappear from the JS output. This means any time you use one, you can be sure that you're not incurring extra JavaScript <-> ReScript conversion cost.

Additionally, no extra ReScript-specific runtime is better for output readability.

Note: do also use externals and the @blabla attributes in the interface files. Otherwise the inlining won't happen.

Design Decisions

ReScript takes interoperating with existing code very seriously. Our type system has very strong guarantees. However, such strong feature also means that, without a great interop system, it'd be very hard to gradually convert a codebase over to ReScript. Fortunately, our interop are comprehensive and cooperate very well with most existing JavaScript code.

The combination of a sound type system + great interop means that we get the benefits of a traditional gradual type system regarding incremental codebase coverage & conversion, without the downside of such gradual type system: complex features to support existing patterns, slow analysis, diminishing return in terms of type coverage, etc.