Docs / GenType / Usage

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:

RE
type 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.