You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ReScript tuple values of type e.g. `(int, string)` are exported as identical JS values of type `[number, string]`. This requires no conversion, unless one of types of the tuple items does.
12
+
While the type of ReScript tuples is immutable, there's currently no mature enforcement in TS, so they're currenty exported to mutable tuples.
13
+
14
+
## Arrays
15
+
16
+
Arrays with elements of ReScript type `t` are exported to JS arrays with elements of the corresponding JS type. If a conversion is required, a copy of the array is performed.
17
+
18
+
Immutable arrays are supported with the additional ReScript library
19
+
[ImmutableArray.res/.resi](https://github.com/reason-association/genType/tree/master/examples/typescript-react-example/src/ImmutableArray.resi), which currently needs to be added to your project.
20
+
The type `ImmutableArray.t<+'a>` is covariant, and is mapped to readonly array types in TS. As opposed to TS, `ImmutableArray.t` does not allow casting in either direction with normal arrays. Instead, a copy must be performed using `fromArray` and `toArray`.
ReScript functions are exported as JS functions of the corresponding type.
12
+
So for example a ReScript function `foo : int => int` is exported as a JS function from numbers to numbers.
13
+
14
+
If named arguments are present in the ReScript type, they are grouped and exported as JS objects. For example `foo : (~x:int, ~y:int) => int` is exported as a JS function from objects of type `{x:number, y:number}` to numbers.
15
+
16
+
In case of mixed named and unnamed arguments, consecutive named arguments form separate groups. So e.g. `foo : (int, ~x:int, ~y:int, int, ~z:int) => int` is exported to a JS function of type `(number, {x:number, y:number}, number, {z:number}) => number`.
17
+
18
+
Function components are exported and imported exactly like normal functions. For example:
ReScript record values of type e.g. `{x: int}` such as `{x: 0}`, `{x: 1}`, are exported to JS values of type `{x: number}` without runtime conversion.
12
+
13
+
Since records are immutable by default, their fields will be exported to readonly property types in TS. Mutable fields are specified in ReScript by e.g. `{mutable mutableField: string}`.
14
+
15
+
The `@as` annotation can be used to change the name of a field on the JS side of things. So e.g. `{@as("y") x: int}` is exported as JS type `{y: number}`.
16
+
17
+
If one field of the ReScript record has option type, this is exported to an optional JS field. So for example ReScript type `{x: option<int>}` is exported as JS type `{x?: number}`.
18
+
19
+
## Objects
20
+
21
+
ReScript object values of type e.g. `{. "x":int}` such as `{"x": 0}`, `{"x": 1}`, `{"x": 2}`, are exported as identical JS object values `{x:0}`, `{x:1}`, `{x:2}`. This requires no conversion. So they are exported to JS values of type `{x:number}`.
22
+
A conversion is required only when the type of some field requires conversions.
23
+
24
+
Since objects are immutable by default, their fields will be exported to readonly property types in TS. Mutable fields are specified in ReScript by e.g. `{ @set "mutableField": string }`.
25
+
26
+
It is possible to mix object and option types, so for example the ReScript type `{. "x":int, "y":option<string>}` exports to JS type `{x:number, ?y: string}`, requires no conversion, and allows option pattern matching on the ReScript side.
ReScript values of type e.g. `option<int>`, such as `None`, `Some(0)`, `Some(1)`, `Some(2)`, are exported to JS values `null`, `undefined`, `0`, `1`, `2`.
12
+
The JS values are unboxed, and `null`/`undefined` are conflated.
13
+
So the option type is exported to JS type `null` or `undefined` or `number`.
14
+
15
+
## Nullables
16
+
17
+
ReScript values of type e.g. `Js.Nullable.t<int>`, such as `Js.Nullable.null`, `Js.Nullable.undefined`, `Js.Nullable.return(0)`, `Js.Nullable.return(1)`, `Js.Nullable.return(2)`, are exported to JS values `null`, `undefined`, `0`, `1`, `2`.
18
+
The JS values are identical: there is no conversion unless the argument type needs conversion.
It's possible to import an existing TS type as an opaque type in ReScript. For example,
12
+
13
+
```res
14
+
@genType.import("./SomeFlowTypes") type weekday
15
+
```
16
+
17
+
defines a type which maps to `weekday` in `SomeFlowTypes.js`.
18
+
See for example [Types.res](https://github.com/reason-association/genType/tree/master/examples/flow-react-example/src/Types.res) and [SomeFlowTypes.js](https://github.com/reason-association/genType/tree/master/examples/flow-react-example/src/SomeFlowTypes.js).
19
+
20
+
## Recursive Types
21
+
22
+
Recursive types which do not require a conversion are fully supported.
23
+
If a recursive type requires a conversion, only a shallow conversion is performed, and a warning comment is included in the output. (The alternative would be to perform an expensive conversion down a data structure of arbitrary size).
24
+
See for example [Types.res](https://github.com/reason-association/genType/tree/master/examples/typescript-react-example/src/nested/Types.res).
25
+
26
+
## First Class Modules
27
+
28
+
ReScript first class modules are converted from their array ReScript runtime representation to JS Object types.
29
+
For example,
30
+
31
+
```res
32
+
module type MT = {
33
+
let x: int
34
+
let y: string
35
+
}
36
+
module M = {
37
+
let y = "abc"
38
+
let x = 42
39
+
}
40
+
41
+
@genType
42
+
let firstClassModule: module(MT) = module(M)
43
+
```
44
+
45
+
is exported as a JS object of type
46
+
47
+
```res
48
+
{"x": number, "y": string}
49
+
```
50
+
51
+
Notice how the order of elements in the exported JS object is determined by the module type `MT` and not the module implementation `M`.
52
+
53
+
## Polymorphic Types
54
+
55
+
If a ReScript type contains a type variable, the corresponding value is not converted. In other words, the conversion is the identity function. For example, a ReScript function of type `{payload: 'a} => 'a` must treat the value of the payload as a black box, as a consequence of parametric polymorphism. If a typed back-end is used, the ReScript type is converted to the corresponding generic type.
56
+
57
+
### Exporting Values from Polymorphic Types with Hidden Type Variables
58
+
59
+
For cases when a value that contains a hidden type variable needs to be converted, a function can be used to produce the appropriate output:
60
+
61
+
**Doesn't work**
62
+
63
+
```res
64
+
@genType
65
+
let none = None
66
+
```
67
+
68
+
```js
69
+
exportconst none: ?T1=OptionBS.none; // Errors out as T1 is not defined
70
+
```
71
+
72
+
**Works**
73
+
74
+
```res
75
+
@genType
76
+
let none = () => None
77
+
```
78
+
79
+
```js
80
+
constnone=<T1>(a:T1):?T1=>OptionBS.none;
81
+
```
82
+
83
+
## Promises
84
+
85
+
Values of type `Js.Promise.t<arg>` are exported to JS promises of type `Promise<argJS>` where `argJS` is the JS type corresponding to `arg`.
86
+
If a conversion for the argument is required, the conversion functions are chained via `.then(promise=>...)`.
Ordinary variants (with capitalized cases, e.g. `| A | B(int)`) and polymorphic variants (with a backtick, e.g. `` | `A | `B(int) ``) are represented in the same way, so there's no difference from the point of view of JavaScript. Polymorphic variants don't have to be capitalized.
10
+
11
+
Variants can have an _unboxed_, or a _boxed_ representation. The unboxed representation is used when there is at most one case with a payload, and that payload has object type; otherwise, a boxed representation is used. Object types are arrays, objects, records and tuples.
12
+
13
+
Variants without payloads are essentially sequences of identifiers.
14
+
E.g. type `@genType type days = Monday | Tuesday`.
15
+
The corresponding JS representation is `"Monday"`, `"Tuesday"`.
16
+
Similarly, polymorphic variant type `@genType type days = [#Monday | #Tuesday]` has the same JS representation.
17
+
18
+
When at most one variant case has a payload, and if the payload is of object type, e.g.
then the representation is unboxed: JS values are e.g. `"Unnamed"` and
21
+
`{name: "hello", surname: "world"}`. Similarly for polymorphic variants.
22
+
Note that this unboxed representation does not use the label `"Named"` of the variant case with payload, because that value is distinguished from the other payload-less cases by its type: an object.
23
+
24
+
If there is more than one case with payload, or if the single payload has not type object, a boxed representation is used. The boxed representation has shape `{tag: "someTag", value: someValue}`.
25
+
For example, type `| A | B(int) | C(string)` has values such as `"A"` and
26
+
`{tag: "B", value: 42}` and `{tag: "C", value: "hello"}`.
27
+
Polymorhphic variants are treated similarly. Notice that payloads for polymorphic variants are always unary: `` `Pair(int,int) `` has a single payload of type `(int,int)`. Instead, ordinary variants distinguish between unary `Pair((int,int))` and binary `Pair(int,int)` payloads. All those cases are represented in JS as `{tag: "Pair", value: [3, 4]}`, and the conversion functions take care of the different ReScript representations.
28
+
29
+
The `@genType.as` annotation can be used to modify the name emitted for a variant case on the JS side. So e.g. `| @genType.as("Arenamed") A` exports ReScript value `A` to JS value `"Arenamed"`.
30
+
Boolean/integer/float constants can be expressed as `| @genType.as(true) True` and `| @genType.as(20) Twenty` and `| @genType.as(0.5) Half`. Similarly for polymorphic variants.
31
+
The `@genType.as` annotation can also be used on variants with payloads to determine what appears in `{ tag: ... }`.
32
+
33
+
For more examples, see [Variants.res](https://github.com/reason-association/genType/tree/master/examples/typescript-react-example/src/Variants.res) and [VariantsWithPayload.res](https://github.com/reason-association/genType/tree/master/examples/typescript-react-example/src/VariantsWithPayload.res).
34
+
35
+
**NOTE:** When exporting/importing values that have polymorphic variant type, you have to use type annotations, and cannot rely on type inference. So instead of `` let monday = `Monday ``, use `` let monday : days = `Monday ``. The former does not work, as the type checker infers a type without annotations.
0 commit comments