[ANN] First release of `ts2ocaml` - generates OCaml bindings from .d.ts files!

Hello,

We’re pleased to announce that ts2ocaml is now public!

This is a tool which parses TypeScript definition files (.d.ts) of a JS package and then generates an OCaml binding for the package.

ts2ocaml currently supports js_of_ocaml as a target via LexiFi/gen_js_api, and ReScript is also going to be supported too!

You can install ts2ocaml from NPM: npm install -g @ocsigen/ts2ocaml.
Please take a look at the documentation on our GitHub repository before using it.

Also, we appreciate any feedback or bug reports, especially since this is the first release of ts2ocaml!

This tool is heavily inspired by ts2fable, which generates Fable (F# AltJS) bindings from .d.ts files. This tool is also written in Fable. Thank you very much for the great language and an awesome ecosystem, Fable team!

Enjoy!

25 Likes

You can find a small real-world example here, and see the GitHub Actions workflow to learn how to build one.
https://discuss.ocaml.org/t/real-world-use-example-of-ts2ocaml

1 Like

Thanks a lot for this!

I haven’t use it yet, but I have a question after reading a bit of doc.

In those cases, this tool will leave them as untyped. It’s also human’s job to (possibly) restore type safety with some other means.

Are those cases marked or signaled in a specific way so that they are easy to detect and “correct” by hand?

In those cases, it will also emit a comment in the format (* FIXME: reason *). So you can search with FIXME to find the untyped parts.

3 Likes

Thanks for releasing this! This addresses an important requirement in the jsoo community.

I was a bit surprised to see ts2ocaml implemented in F#.

I have no problem with F# but I’m curious about why ts2ocaml was not built in OCaml. Right now for people wanting to contribute or understand the project there are too many context switches required:

  • The fsharp codebase converts typescript definitions to ocaml
  • The ocaml definitions are then transpiled to js via jsoo+gen_js_api

In other words, to contribute to this project one has to be conversant with fsharp also in addition to typescript, JavaScript, OCaml.

I worry that this cool project may not get the upgrades and maintenance in the years ahead due to a lack of a large enough pool of people with competences in all these areas…

1 Like
  1. I’m used to it
  2. I needed bindings to TypeScript Compiler API and possibly many other JS packages, but F# already has a tool to generate it
  3. we’re going to support multiple targets (jsoo and ReScript) so I thought it’s safe not to prefer one over another

In short, F# and Fable were needed to bootstrap.

In other words, to contribute to this project one has to be conversant with fsharp also in addition to typescript, JavaScript, OCaml.

That said, as for users, no one needs to be familiar with F#. They just install the JavaScript code generated by Fable via npm/yarn and call it through them.

The lesson is don’t resolve a problem until it’s actually a problem - i.e. worry about it only when a enough number of potential contributors come along.

4 Likes

Actually, the point of this project is we bootstrapped TS to OCaml conversion. Even when this project goes dead, people can still use this to generate a binding for TypeScript Compiler API and any other packages to write a similar tool (which was impossible before this tool).

Also there will be ReScript support too, which is another context switch. This problem is hard to avoid regardless of the language used to implement it…

Is Typescript.fs written all by your team? This is like reading the typescript spec docs. Huge work!

Just checked out ts2fable. I see the inspiration :sunglasses:

As you can guess from the header comment, it was generated by ts2fable. It’s one of the bootstrap meanings we have.

Great to see this problem being addressed! I’ve took a stab at this, but tried bootstrapping in pure JS. Somewhat intermediate version can be found here: Half-arsed highly-PoC level typescript => gen_js_api bindings generator · GitHub. I’ve improved it a bit since this gist, now it’s capable of providing compileable and usable bindings for Cassandra npm package.

Last time I looked at ts2fable - it was working on AST. What I did is walk module exports and use TypeChecker API to get information about types.

With this available, if dune integrates with vite/rollup+esbuild or webpack+swc, I could imagine more OCaml teams choosing jsoo for the front end.

1 Like

That’s too idealistic I believe, but let’s say is possible + people want it + make sense.

How would that work though? Either webpack/rollup has a ton of configuration ad-hoc that doesn’t match how dune is configured.

In the current status is very doable to configure both tools side-by-side and call both concurrently.

Honestly, jsoo doesn’t output ES6 modules, so it’s hard to do things like tree shaking or lazy loading, at least ReScript and Fable are more practical for web development. In the JaneStreet use case, the initial load time doesn’t matter, and once loaded, it just communicates with WS, so jsoo is a bit special and we shouldn’t expect it to be applied to general web application development. But we also have a plan to improve it, so we want it to be better.

1 Like

I’m hacking on this literally right now, for all of the reasons discussed above. We’ll see how far it goes. Two primary components involved:

  1. Supporting an ~esm flag for the jsoo build-runtime cmd
  2. Supporting an ~esm flag for independent compilations to link against the esm runtime

It’s obviously more than just that, but there’s no reason jsoo couldn’t emit esm. The current artifacts aren’t that far away from esm, we just need some alternative ast printing, for the most part. I see ESM artifacts as critical to unlockling integration with the rest of the JS ecosystem. Technically you can pull off OK integrations with current provisions, but there aren’t readily available workflows for making it happen. It’s a pain. Further, we want to exploit the myriad of goodies/fixes that esm brings to the web in general, as you’ve rightly noted.

Anyway, as a primarily typescript developer, I look forward to trying out your bindings :slight_smile:

1 Like