Skip to content

Commit 9889c7d

Browse files
Split up supported types to multiple bind-to-ts files
1 parent d5081a7 commit 9889c7d

9 files changed

+214
-199
lines changed

data/sidebar_manual_latest.json

+6-3
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,13 @@
5656
"TypeScript Interop": [
5757
"gentype-introduction",
5858
"gentype-usage",
59-
"gentype-supported-types",
60-
"bind-to-ts-object",
6159
"bind-to-ts-primitive",
62-
"bind-to-ts-variant"
60+
"bind-to-ts-collections",
61+
"bind-to-ts-object",
62+
"bind-to-ts-option-null",
63+
"bind-to-ts-function",
64+
"bind-to-ts-variant",
65+
"bind-to-ts-other"
6366
],
6467
"Build System": [
6568
"build-overview",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
title: "Bind collections to TS"
3+
description: "Interop with collection types between ReScript and TypeScript"
4+
canonical: "/docs/manual/latest/bind-to-ts-collections"
5+
---
6+
7+
# Bind collections to TS
8+
9+
## Tuples
10+
11+
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`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
title: "Bind to TS Function"
3+
description: "Interop with functions between ReScript and TypeScript"
4+
canonical: "/docs/manual/latest/bind-to-ts-function"
5+
---
6+
7+
# Bind to TS Function
8+
9+
## Functions and Function Components
10+
11+
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:
19+
20+
```rescript
21+
@genType
22+
@react.component
23+
let make = (~name) => React.string(name);
24+
```

pages/docs/manual/latest/bind-to-ts-object.mdx

+19
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,22 @@ canonical: "/docs/manual/latest/bind-to-ts-object"
55
---
66

77
# Bind to TS Object
8+
9+
## Records
10+
11+
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.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
title: "Bind to optional or nullable TS types"
3+
description: "Interop with ReScript option and nullable in TypeScript"
4+
canonical: "/docs/manual/latest/bind-to-ts-option-null"
5+
---
6+
7+
# Bind ReScript option and nullable to TS
8+
9+
## Optionals
10+
11+
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.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
---
2+
title: "More TypeScript interop"
3+
description: "More TypeScript interop"
4+
canonical: "/docs/manual/latest/bind-to-ts-other"
5+
---
6+
7+
# More TypeScript interop
8+
9+
## Imported Types
10+
11+
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+
export const 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+
const none = <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 => ...)`.

pages/docs/manual/latest/bind-to-ts-primitive.mdx

+12
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,15 @@ canonical: "/docs/manual/latest/bind-to-ts-primitive"
55
---
66

77
# Bind to TS Primitives
8+
9+
## Int
10+
11+
ReScript values e.g. `1`, `2`, `3` are unchanged. So they are exported to JS values of type `number`.
12+
13+
## Float
14+
15+
ReScript values e.g. `1.0`, `2.0`, `3.0` are unchanged. So they are exported to JS values of type `number`.
16+
17+
## String
18+
19+
ReScript values e.g. `"a"`, `"b"`, `"c"` are unchanged. So they are exported to JS values of type `string`.

pages/docs/manual/latest/bind-to-ts-variant.mdx

+29-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,32 @@ description: "Interop with ReScript variants in TypeScript"
44
canonical: "/docs/manual/latest/bind-to-ts-variant"
55
---
66

7-
# Bind to TS Object
7+
# Bind ReScript variants to TS
8+
9+
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.
19+
`Unnamed | Named({. "name": string, "surname": string})`
20+
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

Comments
 (0)