Export Scala.js APIs to JavaScript
By default, Scala.js classes, objects, methods and properties are not available
to JavaScript. Entities that have to be accessed from JavaScript must be
annotated explicitly as exported, using @JSExportTopLevel
and @JSExport
.
A simple example
This allows to call the sayHello()
method of HelloWorld
like this in
JavaScript:
The @JSExportTopLevel
on HelloWorld
exports the object HelloWorld
itself
in the JavaScript global scope. It is however not sufficient to allow JavaScript
to call methods of HelloWorld
. This is why we also have to export the
method sayHello()
with @JSExport
.
In general, things that should be exported on the top-level, such as top-level
objects and classes, are exported with @JSExportTopLevel
, while things that
should be exported as properties or methods in JavaScript are exported with
@JSExport
.
Exporting top-level objects
Put on a top-level object, the @JSExportTopLevel
annotation exports that
object to the JavaScript global scope. The name under which it is to be exported
must be specified as an argument to @JSExportTopLevel
.
exports the HelloWorld
object in JavaScript.
Exporting classes
The @JSExportTopLevel
annotation can also be used to export Scala.js classes
to JavaScript (but not traits), or, to be more precise, their constructors. This
allows JavaScript code to create instances of the class.
exposes Foo
as a constructor function to JavaScript:
will log the string "Foo(3)"
to the console. This particular example works
because it calls toString()
, which is always exported to JavaScript. Other
methods must be exported explicitly as shown in the next section.
Exports with modules
When emitting a module for Scala.js code, top-level exports are not sent to the JavaScript global scope.
Instead, they are genuinely exported from the module.
In that case, an @JSExportTopLevel
annotation has the semantics of an ECMAScript 2015 export.
For example:
is semantically equivalent to this JavaScript export:
Exporting methods
Similarly to objects, methods of Scala classes, traits and objects can be
exported with @JSExport
. Unlike for @JSExportTopLevel
, the name argument is
optional for @JSExport
, and defaults to the Scala name of the method.
Given this definition, and some variable foo
holding an instance of Foo
,
you can call:
Overloading
Several methods can be exported with the same JavaScript name (either because
they have the same name in Scala, or because they have the same explicit
JavaScript name as parameter of @JSExport
). In that case, run-time overload
resolution will decide which method to call depending on the number and run-time
types of arguments passed to the the method.
For example, given these definitions:
the following calls will dispatch to each of the three methods:
If the Scala.js compiler cannot produce a dispatching code capable of reliably disambiguating overloads, it will issue a compile error (with a somewhat cryptic message):
gives:
[error] HelloWorld.scala:16: double definition:
[error] method $js$exported$meth$foobar:(i: Int)Any and
[error] method $js$exported$meth$foobar:(y: Int)Any at line 14
[error] have same type
[error] @JSExport("foobar")
[error] ^
[error] one error found
Hint to recognize this error: the methods are named $js$exported$meth$
followed by the JavaScript export name.
Exporting top-level methods
While an @JSExport
ed method inside an @JSExportTopLevel
object allows JavaScript code to call a “static” method,
it does not feel like a top-level function from JavaScript’s point of view.
@JSExportTopLevel
can also be used directly on a method of a top-level
object, which exports the method as a truly top-level function:
can be called from JavaScript as:
Exporting properties
val
s, var
s and def
s without parentheses, as well as def
s whose name
ends with _=
, have a single argument and Unit
result type, are
exported to JavaScript as properties with getters and/or setters
using, again, the @JSExport
annotation.
Given this weird definition of a halfway mutable point:
JavaScript code can use the properties as follows:
As usual, explicit names can be given to @JSExport
. For def
setters, the
JS name must be specified without the trailing _=
.
def
setters must have a result type of Unit
and exactly one parameter. Note
that several def
setters with different types for their argument can be
exported under a single, overloaded JavaScript name.
In case you overload properties in a way the compiler cannot
disambiguate, the methods in the error messages will be prefixed by
$js$exported$prop$
.
Export fields directly declared in constructors
You can export fields directly declared in constructors by annotating the constructor argument:
Export fields to the top level
Similarly to methods, fields (val
s and var
s) of top-level objects can be
exported as top-level variables using @JSExportTopLevel
:
exports bar
and foobar
to the top-level, so that they can be used from
JavaScript as
Note that for var
s, the JavaScript binding is read-only, i.e., JavaScript
code cannot assign a new value to an exported var
. However, if Scala.js code
sets Foo.foobar
, the new value will be visible from JavaScript. This is
consistent with exporting a let
binding in ECMAScript 2015 modules.
Automatically export all members
Instead of writing @JSExport
on every member of a class or object, you may use the @JSExportAll
annotation. It is equivalent to adding @JSExport
on every public (term) member directly declared in the class/object:
This is strictly equivalent to writing:
It is important to note that this does not export inherited members. If you wish to do so, you’ll have to override them explicitly: