diff --git a/data/sidebar_manual_latest.json b/data/sidebar_manual_latest.json index 67bd90667..43a78c47a 100644 --- a/data/sidebar_manual_latest.json +++ b/data/sidebar_manual_latest.json @@ -51,18 +51,8 @@ "use-illegal-identifier-names", "generate-converters-accessors", "browser-support-polyfills", - "libraries" - ], - "TypeScript Interop": [ - "gentype-introduction", - "gentype-usage", - "bind-to-ts-primitive", - "bind-to-ts-collections", - "bind-to-ts-object", - "bind-to-ts-option-null", - "bind-to-ts-function", - "bind-to-ts-variant", - "bind-to-ts-other" + "libraries", + "typescript-integration" ], "Build System": [ "build-overview", diff --git a/pages/docs/manual/latest/bind-to-ts-collections.mdx b/pages/docs/manual/latest/bind-to-ts-collections.mdx deleted file mode 100644 index 1793fbf32..000000000 --- a/pages/docs/manual/latest/bind-to-ts-collections.mdx +++ /dev/null @@ -1,20 +0,0 @@ ---- -title: "Bind collections to TS" -description: "Interop with collection types between ReScript and TypeScript" -canonical: "/docs/manual/latest/bind-to-ts-collections" ---- - -# Bind collections to TS - -## Tuples - -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. -While the type of ReScript tuples is immutable, there's currently no mature enforcement in TS, so they're currenty exported to mutable tuples. - -## Arrays - -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. - -Immutable arrays are supported with the additional ReScript library -[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. -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`. diff --git a/pages/docs/manual/latest/bind-to-ts-function.mdx b/pages/docs/manual/latest/bind-to-ts-function.mdx deleted file mode 100644 index 664a9792f..000000000 --- a/pages/docs/manual/latest/bind-to-ts-function.mdx +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: "Bind to TS Function" -description: "Interop with functions between ReScript and TypeScript" -canonical: "/docs/manual/latest/bind-to-ts-function" ---- - -# Bind to TS Function - -## Functions and Function Components - -ReScript functions are exported as JS functions of the corresponding type. -So for example a ReScript function `foo : int => int` is exported as a JS function from numbers to numbers. - -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. - -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`. - -Function components are exported and imported exactly like normal functions. For example: - -```rescript -@genType -@react.component -let make = (~name) => React.string(name); -``` diff --git a/pages/docs/manual/latest/bind-to-ts-object.mdx b/pages/docs/manual/latest/bind-to-ts-object.mdx deleted file mode 100644 index 227383dcd..000000000 --- a/pages/docs/manual/latest/bind-to-ts-object.mdx +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: "Bind to TS Object" -description: "Interop with TS objects in ReScript" -canonical: "/docs/manual/latest/bind-to-ts-object" ---- - -# Bind to TS Object - -## Records - -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. - -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}`. - -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}`. - -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}` is exported as JS type `{x?: number}`. - -## Objects - -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}`. -A conversion is required only when the type of some field requires conversions. - -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 }`. - -It is possible to mix object and option types, so for example the ReScript type `{. "x":int, "y":option}` exports to JS type `{x:number, ?y: string}`, requires no conversion, and allows option pattern matching on the ReScript side. diff --git a/pages/docs/manual/latest/bind-to-ts-option-null.mdx b/pages/docs/manual/latest/bind-to-ts-option-null.mdx deleted file mode 100644 index bf27a3699..000000000 --- a/pages/docs/manual/latest/bind-to-ts-option-null.mdx +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: "Bind to optional or nullable TS types" -description: "Interop with ReScript option and nullable in TypeScript" -canonical: "/docs/manual/latest/bind-to-ts-option-null" ---- - -# Bind ReScript option and nullable to TS - -## Optionals - -ReScript values of type e.g. `option`, such as `None`, `Some(0)`, `Some(1)`, `Some(2)`, are exported to JS values `null`, `undefined`, `0`, `1`, `2`. -The JS values are unboxed, and `null`/`undefined` are conflated. -So the option type is exported to JS type `null` or `undefined` or `number`. - -## Nullables - -ReScript values of type e.g. `Js.Nullable.t`, 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`. -The JS values are identical: there is no conversion unless the argument type needs conversion. diff --git a/pages/docs/manual/latest/bind-to-ts-other.mdx b/pages/docs/manual/latest/bind-to-ts-other.mdx deleted file mode 100644 index a05d0d297..000000000 --- a/pages/docs/manual/latest/bind-to-ts-other.mdx +++ /dev/null @@ -1,86 +0,0 @@ ---- -title: "More TypeScript interop" -description: "More TypeScript interop" -canonical: "/docs/manual/latest/bind-to-ts-other" ---- - -# More TypeScript interop - -## Imported Types - -It's possible to import an existing TS type as an opaque type in ReScript. For example, - -```res -@genType.import("./SomeFlowTypes") type weekday -``` - -defines a type which maps to `weekday` in `SomeFlowTypes.js`. -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). - -## Recursive Types - -Recursive types which do not require a conversion are fully supported. -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). -See for example [Types.res](https://github.com/reason-association/genType/tree/master/examples/typescript-react-example/src/nested/Types.res). - -## First Class Modules - -ReScript first class modules are converted from their array ReScript runtime representation to JS Object types. -For example, - -```res -module type MT = { - let x: int - let y: string -} -module M = { - let y = "abc" - let x = 42 -} - -@genType -let firstClassModule: module(MT) = module(M) -``` - -is exported as a JS object of type - -```res -{"x": number, "y": string} -``` - -Notice how the order of elements in the exported JS object is determined by the module type `MT` and not the module implementation `M`. - -## Polymorphic Types - -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. - -### Exporting Values from Polymorphic Types with Hidden Type Variables - -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: - -**Doesn't work** - -```res -@genType -let none = None -``` - -```js -export const none: ?T1 = OptionBS.none; // Errors out as T1 is not defined -``` - -**Works** - -```res -@genType -let none = () => None -``` - -```js -const none = (a: T1): ?T1 => OptionBS.none; -``` - -## Promises - -Values of type `Js.Promise.t` are exported to JS promises of type `Promise` where `argJS` is the JS type corresponding to `arg`. -If a conversion for the argument is required, the conversion functions are chained via `.then(promise => ...)`. diff --git a/pages/docs/manual/latest/bind-to-ts-primitive.mdx b/pages/docs/manual/latest/bind-to-ts-primitive.mdx deleted file mode 100644 index 0bb19b279..000000000 --- a/pages/docs/manual/latest/bind-to-ts-primitive.mdx +++ /dev/null @@ -1,19 +0,0 @@ ---- -title: "Bind to TS primitive values" -description: "Interop with TS primitives in ReScript" -canonical: "/docs/manual/latest/bind-to-ts-primitive" ---- - -# Bind to TS Primitives - -## Int - -ReScript values e.g. `1`, `2`, `3` are unchanged. So they are exported to JS values of type `number`. - -## Float - -ReScript values e.g. `1.0`, `2.0`, `3.0` are unchanged. So they are exported to JS values of type `number`. - -## String - -ReScript values e.g. `"a"`, `"b"`, `"c"` are unchanged. So they are exported to JS values of type `string`. diff --git a/pages/docs/manual/latest/bind-to-ts-variant.mdx b/pages/docs/manual/latest/bind-to-ts-variant.mdx deleted file mode 100644 index d750ed5e2..000000000 --- a/pages/docs/manual/latest/bind-to-ts-variant.mdx +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: "Bind variants to TS" -description: "Interop with ReScript variants in TypeScript" -canonical: "/docs/manual/latest/bind-to-ts-variant" ---- - -# Bind ReScript variants to TS - -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. - -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. - -Variants without payloads are essentially sequences of identifiers. -E.g. type `@genType type days = Monday | Tuesday`. -The corresponding JS representation is `"Monday"`, `"Tuesday"`. -Similarly, polymorphic variant type `@genType type days = [#Monday | #Tuesday]` has the same JS representation. - -When at most one variant case has a payload, and if the payload is of object type, e.g. -`Unnamed | Named({. "name": string, "surname": string})` -then the representation is unboxed: JS values are e.g. `"Unnamed"` and -`{name: "hello", surname: "world"}`. Similarly for polymorphic variants. -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. - -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}`. -For example, type `| A | B(int) | C(string)` has values such as `"A"` and -`{tag: "B", value: 42}` and `{tag: "C", value: "hello"}`. -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. - -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"`. -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. -The `@genType.as` annotation can also be used on variants with payloads to determine what appears in `{ tag: ... }`. - -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). - -**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. diff --git a/pages/docs/manual/latest/build-configuration.mdx b/pages/docs/manual/latest/build-configuration.mdx index 485753177..3e2e3c474 100644 --- a/pages/docs/manual/latest/build-configuration.mdx +++ b/pages/docs/manual/latest/build-configuration.mdx @@ -192,6 +192,41 @@ The warning numbers are shown in the build output when they're triggered. See [W Extra flags to pass to the compiler. For advanced usages. +## gentypeconfig + +To enable genType, set `"gentypeconfig"` at top level in the project's `rescript.json`. + +```json +{ + "gentypeconfig": { + "module": "es6", + "moduleResolution": "node", + "generatedFileExtension": ".gen.tsx", + "debug": { + "all": false, + "basic": false + } + } +} +``` + +`generatedFileExtension`: File extension used for genType generated files (defaults to `".gen.tsx"`) + +`module`: Module format used for the generated `*.gen.tsx` files (supports `"es6"` and `"commonjs"`) + +`moduleResolution`: Module resolution strategy used in genType outputs. This may be required for compatibility with TypeScript projects. Specify the value as the same in `tsconfig.json`. +- `"node"`(default): Drop extensions in import paths. +- `"node16"`: Use TS output's extension. This provides compatibility with projects using `"moduleResolution": "node16"` and ES Modules. +- `"bundler"`: Use TS input's extension. This provides compatibility with projects using `"moduleResolution": "bundler"` and ES Modules. This also requires TS v5.0+ and `compilerOptions.allowImportingTsExtensions` to `true` + +`debug`: Enable debug logs. + +### Deprecated options + +`language`: the `language` setting is not required from compiler v10.1. + +`shims`: Required only if one needs to export certain basic ReScript data types to JS when one cannot modify the sources to add annotations (e.g. exporting ReScript lists), and if the types are not first-classed in genType. + ## Environment Variables We heavily disrecommend the usage of environment variables, but for certain cases, they're justified. diff --git a/pages/docs/manual/latest/gentype-introduction.mdx b/pages/docs/manual/latest/gentype-introduction.mdx deleted file mode 100644 index 3f7165a6d..000000000 --- a/pages/docs/manual/latest/gentype-introduction.mdx +++ /dev/null @@ -1,181 +0,0 @@ ---- -title: "Introduction" -description: "GenType - Interoperability between ReScript and TypeScript" -canonical: "/docs/manual/latest/gentype-introduction" ---- - -# ReScript & TypeScript - -The ReScript compiler includes a code generation tool that lets you export ReScript values and types to use in TypeScript (TS), and import TS values and types into ReScript. It is called `genType`. - -Converter functions between the two runtime representations are generated when required based on the type of the values. -In particular, conversion of [rescript-react](/docs/react/latest/introduction) components both ways is supported, with automatic generation of the wrappers. - -The implementation of genType performs a type-directed transformation of ReScript programs after compilation. The transformed programs operate on data types idiomatic to JS. - -For example, a ReScript function operating on a ReScript variant `type t = | A(int) | B(string)` (which is represented as custom objects with tags at runtime) is exported to a JS function operating on the corresponding JS object of type `{ TAG: "A"; _0: number } | { TAG: "B"; _0: string };`. - -## A Quick Example - -Let's assume we are working on a TypeScript codebase and we want to integrate a single rescript-react component. - -We want to be able to import the rescript-react component like any other React component in our existing TS code, but we also want to preserve all the ReScript types in the TS type system (and convert incompatible values if necessary). - -**That's exactly what genType was made for!** - -First we'll set up a rescript-react component: - -```res -/* src/MyComp.res */ - -@genType -type color = - | Red - | Blue - -@genType -@react.component -let make = (~name: string, ~color: color) => { - let colorStr = - switch (color) { - | Red => "red" - | Blue => "blue" - } - -
{React.string(name)}
-} -``` - -On a successful compile, `genType` will convert `src/MyComp.res` to a TS file called `src/MyComp.gen.tsx` which will look something like this: - -```ts -// src/MyComp.gen.tsx - -/* TypeScript file generated from Main.res by genType. */ - -/* eslint-disable */ -/* tslint:disable */ - -import * as React from "react"; - -import * as MainBS__Es6Import from "./Main.res"; -const MainBS: any = MainBS__Es6Import; - -export type color = "Red" | "Blue"; - -export type Props = { readonly color: color; readonly name: string }; - -export const make: React.ComponentType<{ - readonly color: color; - readonly name: string; -}> = MainBS.make; -``` - -genType automatically maps the `color` variant to TS via a string union type `color = "Red" | "Blue"`, and also provides all the converters to convert between the ReScript & TS representation as well. - -Therefore we can seamlessly use ReScript specific data structures within TS without writing the converter code by hand! - -Within our TypeScript application, we can now import and use the React component in the following manner: - -```ts -// src/App.ts -import { make as MyComp } from "./MyComp.gen.tsx"; - -const App = () => { - return ( -
-

My Component

- -
- ); -}; -``` - -## Setup - -Add a `gentypeconfig` section to your `rescript.json` (See [Configuration](#configuration) for details): - -```json -"gentypeconfig": { - "language": "typescript", - "shims": {}, - "generatedFileExtension": ".gen.tsx", - "module": "es6", - "debug": { - "all": false, - "basic": false - } -} -``` - -## Configuration - -Every `genType` powered project requires a configuration item `"gentypeconfig"` -at top level in the project's `rescript.json`. The configuration has following -structure: - -```json - //... - "gentypeconfig": { - "language": "typescript", - "generatedFileExtension": ".gen.tsx", - "module": "es6" | "commonjs", - "shims": { - "ReasonReact": "ReactShim" - } - } -``` - -- **generatedFileExtension** - - - File extension used for genType generated files (defaults to `.gen.tsx`) - -- **language** - - - `"typescript"` : the `language` setting is not required from compiler v10.1 - -- **module** - - - Module format used for the generated `*.gen.tsx` files (supports `"es6"` and `"commonjs"`) - -- **shims** - - Required only if one needs to export certain basic ReScript data types to JS when one cannot modify the sources to add annotations (e.g. exporting ReScript lists), and if the types are not first-classed in genType. - - Example: `Array` with format: `"RescriptModule=JavaScriptModule"` - -## Adding Shims - -A shim is a TS file that provides user-provided definitions for library types. - -Configure your shim files within `"gentypeconfig"` in your [`rescript.json`](https://github.com/rescript-lang/rescript-compiler/blob/master/jscomp/gentype_tests/typescript-react-example/rescript.json), and add relevant `.shims.ts` files in a directory which is visible by ReScript e.g. [`src/shims/`](https://github.com/rescript-lang/rescript-compiler/tree/master/jscomp/gentype_tests/typescript-react-example/src/shims). An example shim to export ReactEvent can be found [here](https://github.com/rescript-lang/rescript-compiler/blob/master/jscomp/gentype_tests/typescript-react-example/src/shims/ReactEvent.shim.ts). - -## Testing the Whole Setup - -Open any relevant `*.res` file and add `@genType` annotations to any bindings / values / functions to be used from JavaScript. If an annotated value uses a type, the type must be annotated too. See e.g. [Hooks.res](https://github.com/reason-association/genType/blob/master/examples/typescript-react-example/src/Hooks.res). - -Save the file and rebuild the project via `npm run bs:build` or similar. You should now see a `*.gen.tsx` file with the same name (e.g. `MyComponent.res` -> `MyComponent.gen.tsx`). - -Any values exported from `MyComponent.res` can then be imported from JS. For example: - -```js -import MyComponent from "./components/MyComponent.gen"; -``` - -## Examples - -We prepared some examples to give you an idea on how to integrate `genType` in your own project. Check out the READMEs of the listed projects. - -- [typescript-react-example](https://github.com/rescript-lang/rescript-compiler/tree/master/jscomp/gentype_tests/typescript-react-example) - -## Experimental features - -These features are for experimentation only. They could be changed/removed any time, and not be considered breaking changes. - -- Export object and record types as interfaces. To activate, add `"exportInterfaces": true` to the configuration. The types are also renamed from `name` to `Iname`. - -- Emit prop types for the untyped back-end. To activate, add `"propTypes": true` and `"language": "untyped"` to the configuration. - -## Limitations - -- **in-source = true**. Currently only supports ReScript projects with [in-source generation](/docs/manual/latest/build-configuration#package-specs) and file suffixes that end on `.js`, like `.res.js` or `.bs.js`. - -- **Limited namespace support**. Currently there's limited [namespace](/docs/manual/latest/build-configuration#name-namespace) support, and only `namespace:true` is possible, not e.g. `namespace:"custom"`. diff --git a/pages/docs/manual/latest/gentype-usage.mdx b/pages/docs/manual/latest/gentype-usage.mdx deleted file mode 100644 index 6e79c1486..000000000 --- a/pages/docs/manual/latest/gentype-usage.mdx +++ /dev/null @@ -1,189 +0,0 @@ ---- -title: "Usage" -description: "GenType - Interoperability between ReScript and TypeScript" -canonical: "/docs/manual/latest/gentype-usage" ---- - -# 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](https://github.com/cristianoc/genType/blob/v2.16.0/README.md). - - - -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; -``` - -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; -``` - -## 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. diff --git a/pages/docs/manual/latest/typescript-integration.mdx b/pages/docs/manual/latest/typescript-integration.mdx new file mode 100644 index 000000000..40adcf0bf --- /dev/null +++ b/pages/docs/manual/latest/typescript-integration.mdx @@ -0,0 +1,196 @@ +--- +title: "TypeScript" +description: "GenType - Interoperability between ReScript and TypeScript" +canonical: "/docs/manual/latest/typescript-integration" +--- + +# ReScript & TypeScript + +The ReScript compiler includes a code generation tool that lets you export ReScript values and types to use in TypeScript, and import TypeScript values and types into ReScript. It is called "genType". + +The implementation of genType performs a type-directed transformation of ReScript programs after compilation. The transformed programs operate on data types idiomatic to TypeScript. + +For example, a ReScript variant (which is represented as custom objects with tags at runtime): + +```res +@genType +type t = | A(int) | B(string) +``` + +is exported to a TypeScript type: + +```ts +type t = { TAG: "A"; _0: number } | { TAG: "B"; _0: string }; +``` + +## A Quick Example + +Let's assume we are working on a TypeScript codebase and we want to integrate a single TypeScript function. + +We want to be able to import the function like any other in our existing TypeScript code, but we also want to preserve all the ReScript types in the TypeScript type system + +**That's exactly what genType was made for!** + +First we'll set up a function: + +```res +// src/Color.res + +@genType +type color = + | Red + | Blue + +@genType +let printColorMessage = (~color, ~message) => { + let prefix = switch color { + | Red => "\x1b[91m" + | Blue => "\x1b[94m" + } + let reset = "\x1b[0m" + + Console.log(prefix ++ message ++ reset) +} + +``` + +On a successful compile, `genType` will convert `src/Color.res` to a TypeScript file called `src/Color.gen.tsx` which will look something like this: + +```ts +// src/Color.gen.tsx + +/* TypeScript file generated from Color.res by genType. */ + +/* eslint-disable */ +/* tslint:disable */ + +import * as ColorJS from './Color.res.js'; + +export type color = "Red" | "Blue"; + +export const printColorMessage: (color:color) => void = ColorJS.printColorMessage as any; +``` + +genType automatically maps the `color` variant to TS via a string union type `"Red" | "Blue"`. + +Within our TypeScript application, we can now import and use the function in the following manner: + +```ts +// src/app.ts + +import { printColorMessage } from "./Color.gen.tsx"; + +printColorMessage("Red", "Hello, genType!"); +``` + +## Setup + +Add a `gentypeconfig` section to your `rescript.json` (See [Configuration](/docs/manual/latest/build-configuration#gentypeconfig) for details). + +Every `genType` powered project requires a configuration item `"gentypeconfig"` at top level in the project's `rescript.json`. + +The minimal configuration of genType is following: + +```json +{ + "gentypeconfig": { + "module": "es6", + "moduleResolution": "node", + "generatedFileExtension": ".gen.tsx" + } +} +``` + +And don't forget to make sure `allowJs` in the project `tsconfig.json`: + +```json +{ + "compilerOptions": { + "allowJs": true + } +} +``` + +### TypeScript Module Resolutions + +Make sure to set the same `moduleResolution` in the both `rescript.json` and `tsconfig.json` so that the output of genType is performed at preferred module resolution. + +For example if the TypeScript project uses ES Modules with `Node16` / `NodeNext` module resolution: + +```json +// tsconfig.json +{ + "compilerOptions": { + "moduleResolution": "node16" + } +} +``` + +Then `moduleResolution` in `gentypeconfig` should be same value: + +```json +// rescript.json +{ + "gentypeconfig": { + "moduleResolution": "node16" + } +} +``` + +In case of the TypeScript project uses `Bundler` module resolution, `allowImportingTsExtensions` should also be `true`: + +```json +// tsconfig.json +{ + "compilerOptions": { + "moduleResolution": "bundler", + "allowImportingTsExtensions": true + } +} +``` + +```json +// rescript.json +{ + "gentypeconfig": { + "moduleResolution": "bundler" + } +} +``` + +## Testing the Whole Setup + +Open any relevant `*.res` file and add `@genType` annotations to any bindings / values / functions to be used from JavaScript. If an annotated value uses a type, the type must be annotated too. See e.g. [Hooks.res](https://github.com/rescript-lang/rescript-compiler/blob/master/jscomp/gentype_tests/typescript-react-example/src/Hooks.res). + +Save the file and rebuild the project via `npm run res:build` or similar. You should now see a `*.gen.tsx` file with the same name (e.g. `MyComponent.res` -> `MyComponent.gen.tsx`). + +Any values exported from `MyComponent.res` can then be imported from TypeScript. For example: + +```js +import MyComponent from "./components/MyComponent.gen.tsx"; +``` + +## Experimental features + +These features are for experimentation only. They could be changed/removed any time, and not be considered breaking changes. + +- Export object and record types as interfaces. To activate, add `"exportInterfaces": true` to the configuration. The types are also renamed from `name` to `Iname`. + +## Deprecated features + +Features related to generating runtimes were deprecated since v11 and should no longer be used. + +- **`@genType("alias")`** and **`@genType.as("alias")`** +- **`@genType.opaque`** +- **`@genType.import`** +- TypeScript Shims + +genType does not generate anything runtime-related, and in the near future it generates definition files (`*.d.ts`) directly (See the [roadmap](https://github.com/rescript-lang/rescript-compiler/issues/6196)). + +If any runtime code is required for interoperability with JavaScript / TypeScript projects, it can be written by hand, or request a relevant features (e.g. `@deriving`) to the compiler. + +## Limitations + +- **in-source = true**. Currently only supports ReScript projects with [in-source generation](/docs/manual/latest/build-configuration#package-specs) and file suffixes that end on `.js`, like `.res.js` or `.bs.js`. + +- **Limited namespace support**. Currently there's limited [namespace](/docs/manual/latest/build-configuration#name-namespace) support, and only `namespace:true` is possible, not e.g. `namespace:"custom"`.