Usage
genType
operates on two kinds of entities: types and values.
Each can be exported from Reason to JS, or imported into Reason from JS.
The main annotation is @genType
, which by default means export.
Export and Import Types
The following exports a function type callback
to JS:
RE[@genType]
type callback = ReactEvent.Mouse.t => unit;
To instead import a type called complexNumber
from JS module MyMath.ts
(or MyMath.js
), use the @genType.import
annotation:
RE[@genType.import "./MyMath"]
type complexNumber;
This imported type will be treated as opaque by Reason.
Export and Import Values
To export a function callback
to JS:
RE[@genType]
let callback = _ => Js.log("Clicked");
To rename the function and export it as CB
on the JS side, use
RE[@genType]
[@genType.as "CB"]
let callback = _ => Js.log("Clicked");
or the more compact
RE[@genType "CB"]
let callback = _ => Js.log("Clicked");
To import a function realValue
from JS module MyMath.ts
(or MyMath.js
):
RE[@genType.import "./MyMath"] /* JS module to import from. */
/* Name and type of the JS value to import. */
external realValue: complexNumber => float = "";
Note: With genType < 2.17.0 or bucklescript < 5.0.0, one had to add a line
with @bs.module
and the current file name. See the older
README.
Because of the external
keyword, it's clear from context that this is an import, so you can also just use @genType
and omit .import
.
To import a default JS export, use a second argument to @genType.import
e.g. [@genType.import ("./MyMath", "default")]
.
Similarly, to import a value with a different JS name, use e.g. [@genType.import ("./MyMath", "ValueStartingWithUpperCaseLetter")]
.
To import nested values, e.g. Some.Nested.value
, use e.g. [@genType.import ("./MyMath", "Some.Nested.value")]
.
Export and Import React Components Using Deprecated Record API
To export a ReasonReact component to JS, and automatically generate a wrapper for it, simply annotate the make
function:
RE[@genType]
let make = (~onClick: callback, _children) => {
...component,
render: _ => <div onClick> "Click me"->ReasonReact.string </div>,
};
Note: the value component
must also be defined, above make
in the same module (also in the case of components defined in nested modules).
To import and wrap a ReactJS component for use by ReasonReact, the type of the make
function is the only information required:
RE[@genType.import "./MyBanner"] /* Module with the JS component to be wrapped. */
/* The make function will be automatically generated from the types below. */
external make:
(~show: bool, ~message: option(message)=?, 'a) =>
ReasonReact.component(
ReasonReact.stateless,
ReasonReact.noRetainedProps,
ReasonReact.actionless,
) =
"";
The type of make
must have a named argument for each prop in the JS component. Optional props have option type. The make
function will be generated by genType
.
This assumes that the JS component was exported with a default export. In case of named export, use e.g. [@genType.import ("./MyBanner", "componentName")]
. To import a nested component, use e.g. [@genType.import ("./MyBanner", "Some.Nested.component")]
.
Interface (.rei) and Implementation (.re) files
If both Foo.rei
and Foo.re
exist, the annotations are taken from Foo.rei
.
The behaviour can be overridden by adding annotation [@genType.ignoreInterface];
at the top of Foo.rei
. Use case: expose implementation details to JS but not to Reason.
Type Expansion and @genType.opaque
If an exported type persons
references other types in its definition, those types are also exported by default, as long as they are defined in the same file:
REtype name = string;
type surname = string;
type person = {name:name, surname:surname};
[@genType]
type persons = array(person);
If however you wish to hide from JS the fact that name
and surname
are strings, you can do it with the @genType.opaque
annotation:
RE[@genType.opaque]
type name = string;
[@genType.opaque]
type surname = string;
type person = {
name,
surname,
};
[@genType]
type persons = array(person);
Renaming, @genType.as, and object mangling convention.
By default, entities with a given name are exported/imported with the same name. However, you might wish to change the appearence of the name on the JS side.
Note: From bucklescript 7.0.0, @genType.as
on record fields will be discouraged,
as it incurs a runtime conversion cost. Instead @bs.as
will be supported and incur zero cost.
For example, in case of a record field whose name is a keyword, such as type
:
RE[@genType]
type shipment = {
date: float,
[@genType.as "type"]
type_: string,
};
Object field names follow bucklescript's mangling convention:
Remove trailing "__" if present. Otherwise remove leading "_" when followed by an uppercase letter, or keyword.
This means that the analogous example with objects is:
RE[@genType]
type shipment = {
.
"date": float,
"_type": string,
};
or the equivalent "type__": string
.
Functions and function components also follow the mangling convention for labeled arguments:
RE[@genType]
let exampleFunction = (~_type) => "type: " ++ _type;
[@genType]
[@react.component]
let exampleComponent = (~_type) => React.string("type: " ++ _type);
It is possible to use @genType.as
for functions, though this is only maintained for backwards compatibility, and cannot be used on function components:
RE[@genType]
let functionWithGenTypeAs =
(~date: float) => [@genType.as "type"] (~type_: string) => ...
Note: For technical reasons, it is not possible to use @genType.as
on the first argument of a function (restriction lifted on OCaml 4.0.6).
Dependent projects/libraries
Bucklescript dependencies are specified in bs-dependencies
.
For example, if the dependencies are "bs-dependencies": ["somelibrary"]
and somelibrary
contains Common.re
, this looks up the types of foo
in the library:
RE [@genType]
let z = Common.foo;
Scoped packages of the form e.g. @demo/somelibrary
are also supported.
Note: The library must have been published with the .gen.ts
files created by genType.