Rescript -> typescript -> js structural typing issue

Hi,

I try rescript on a little PoC project and in order to have a chance to integrate it in typescript environments, I explore interoperability between the 2 languages.

Everything seemed to work smoothly when I bumped into an issue when handling rescript sum types on the ts side (in fact, on the transpiled js side).

I use ts-pattern in order to pattern-match on the output of a rescript function from typescript (but the exact same problem arises with a standard switch statement).

In my case it’s based on a Belt.Result.t<'a, 'e>.

the generated typescript (gen.ts) gives me a

type MyResult<a,e> = 
    { tag: "Ok"; value: a }
  | { tag: "Error"; value: e };

so I can pattern match on it (pure typescript side) with

match(result)
     .with({ tag: "Ok" }, () => ...)
     .with({ tag: "Error"}, () => ...)
     .exhaustive()

seemed great, typescript is happy… until I run the code : it breaks at runtime because it receives something like {"TAG":1,"_0":0}

Do you know how I can overcome this (without handling the result in rescript).

NB: I cross-posted as a comment on a similar issue on the gentype github : https://github.com/rescript-association/genType/issues/585, sorry for the noise if you saw it there too.

Best,
Matthieu

I think I’m missing something : I did a minimal example to replicate and it works fine.

I’ll try to figure out the difference with the problematic code and update this thread in case someone else bumps into this.

here’s a minimal example that reproduces the issue : https://github.com/err0r500/rescript-ts-issue-minimal

looks like it happens when a value of a rescript type (the type is exported with @genType) is passed to typescript.

EDIT : ok, I found a workaround by treating the ts code as IO in Haskell : once you’re in, you don’t leave :smiley:

1 Like

From your edit it looks like you figured it out, but you have a problem here.

type t = Belt.Result.t<int, string> => unit
@module("./index") external idontwork: t = "idontwork"

The problem is that you’re telling the compiler that the external idontwork function takes a Belt.Result.t<int, string>, when really it doesn’t. It takes this thing:

export type resType = 
    { tag: "Ok"; value: number }
  | { tag: "Error"; value: string };

Which is not the way that ReScript variants are implemented.

thanks @Ryan that’s the kind of issue I was suspecting.

So I guess I should definitely abandon my idea to go back & forth between rescript & typescript like : rescript with gentype → typescript + rescript with interop → rescript.

That makes sense, actually.
thanks again!

1 Like