GenType Usage

genType operates on two kinds of entities: types and values. Each can be exported from ReScript to JS, or imported into ReScript 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:

RES
@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:

RES
@genType.import("./MyMath") type complexNumber

This imported type will be treated as opaque by ReScript.

Export and Import Values

To export a function callback to JS:

RES
@genType let callback = _ => Js.log("Clicked");

To rename the function and export it as CB on the JS side, use

RES
@genType @genType.as("CB") let callback = _ => Js.log("Clicked");

or the more compact

RES
@genType("CB") let callback = _ => Js.log("Clicked");

To import a function realValue from JS module MyMath.ts (or MyMath.js):

RES
@genType.import("./MyMath") /* JS module to import from. */ /* Name and type of the JS value to import. */ external realValue: complexNumber => float = "realValue";

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")).

Interface (.resi) and Implementation (.res) files

If both Foo.resi and Foo.res exist, the annotations are taken from Foo.resi. The same happens with local modules: if present, the module type gets precedence.

The behaviour can be overridden by adding annotation @genType.ignoreInterface at the top of Foo.resi. Use case: expose implementation details to JS but not to ReScript.

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:

RES
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:

RES
@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.

NOTE: Starting from ReScript 7.0.0, @genType.as on record fields will be discouraged, as it incurs a runtime conversion cost. Use a runtime free @as instead.

NOTE: Starting from ReScript 11.0.0, the object mangling is removed.

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.

For example, to use a reserved keyword type as a record field:

RES
@genType type shipment = { date: float, @genType.as("type") type_: string, }

Object field names follow ReScript'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:

RES
@genType type shipment = { "date": float, "_type": string, }

or the equivalent "type__": string.

Functions and function components also follow the mangling convention for labeled arguments:

RES
@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:

RES
@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.

Dependent Projects / Libraries

ReScript dependencies are specified in bs-dependencies. For example, if the dependencies are "bs-dependencies": ["somelibrary"] and somelibrary contains Common.res, this looks up the types of foo in the library:

RES
@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.