Docs / GenType / Usage

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

Export and Import React Components Using Deprecated Record API

Important: This section is only relevant for legacy reason-react codebases.

To export a ReasonReact component to JS, and automatically generate a wrapper for it, simply annotate the make function:

RES
@genType let make = (~onClick: callback, _children) => { ...component, render: _ => <div onClick> {ReasonReact.string("Click me")} </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:

RES
@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, > = "make";

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 (.resi) and Implementation (.res) files

If both Foo.resi and Foo.res exist, the annotations are taken from Foo.resi.

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.

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: Starting from ReScript 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:

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.