I'm very excited to have finally figured out a pretty solid way to work with .
Disclaimer: Do not do this if you're publishing the generated JS code (f.e. to npm). This will leave invalid references in your JS Doc comments!
This is however fine if you're publishing a gleam package or building an application.
The "trick" is to use import annotation that link to the javascript in the build/dev/javascript directory.
Here is a simplified setup from my https://github.com/daniellionel01/glaze/tree/main/glaze_oat project.
Project structure (I removed all non-relevant modules):
.
├── gleam.toml
├── manifest.toml
├── README.md
└── src
└── glaze
├── oat
│ ├── globals.d.ts
│ ├── toast_ffi.mjs
│ ├── toast.gleam
│ ├── toast.mjs.d.ts
└── oat.gleam
globals.d.ts contains a definition for ot which is the toast library I'm using.
toast.gleam (simplified)
// ... Placement Type ...
// ... Variant Type ...
// used in `toast_ffi.mjs`
pub fn variant_to_string(variant: Variant) { ... }
pub fn placement_to_string(placement: Placement) { ... }
pub opaque type Options {
Options(variant: Variant, placement: Placement, duration_ms: Int)
}
@external(javascript, "./toast_ffi.mjs", "toast")
pub fn toast(title: String, description: String, options: Options) -> Nil
toast_ffi.mjs
//@ts-check
import { variant_to_string, placement_to_string } from "./toast.mjs";
/**
* @param {string} title - The main heading text of the toast notification.
* @param {string} description - Additional descriptive text shown below the title.
* @param {import("../../../build/dev/javascript/glaze_oat/glaze/oat/toast.mjs").Options} options
*
* @returns {void}
*/
export function toast(title, description, options) {
let options_args = {
variant: variant_to_string(options.variant),
placement: placement_to_string(options.placement),
duration: options.duration_ms,
};
ot.toast(title, description, options_args);
}
// toast.mjs.d.ts
export * from "../../../build/dev/javascript/glaze_oat/glaze/oat/toast.d.mts";
Without the toast.mjs.d.ts file the import in toast_ffi.mjs from ./toast.mjs will fail.
And in toast_ffi.mjs we link the generated toast.mjs file, since that contains the Options class.
Also you need to enable typescript declarations in gleam.toml if you're working with opaque types.
In my particular case that was because Options is opaque and without typescript declarations it is not exported.
[javascript]
typescript_declarations = true
As long as we build the project regularly, this will keep the references accurate.
Only hickup that is left are the deprecation warnings, because Options is a custom type and that constructor has been deprecated with the new JS FFI API.
I'm not sure if we can do anything about this.
So yeah, quite a bit of work, but it feels very nice to have solid auto completion and warnings 