Misunderstanding docs on safety from type annotations?

Hello! I am considering adding ReScript to an app I’m working and started a test project to see if I could get some things running with our lab’s current web tools (Vue, D3). It’s worth noting I’m pretty new to web dev; I’ve mostly done Data Science/Computational Bio using Python, R, Perl, and bits of Java and Scala but we’ve added some D3.js to our workflow and people really like the results so I’m leaning into it. So, sorry in advance if I am missing something obvious - I couldn’t find what I was looking for after a few hours and this seemed like the place to go.

I’m a little confused by this section of the docs:

"But you can also optionally write down the type, aka annotate your value:

let score: int = 10

If the type annotation for score doesn’t correspond to our inferred type for it, we’ll show you an error during compilation time. We won’t silently assume your type annotation is correct, unlike many other languages."

I know the type annotations are optional but I can’t help but take from this passage that they’re doing something (that could result in a compiler error). My misunderstanding is from looking at the JS code generated by these ReScript snippets:

// rescript
let typedIntSum = (a:int, b:int): int => a + b;

let typedIntSumNoAnnotation = (a, b) => a + b;

Which gives me:

// js
function typedIntSum(a, b) {
  return a + b | 0;
}

function typedIntSumNoAnnotation(a, b) {
  return a + b | 0;
}

I’m not sure where the annotations have done anything (besides the usefulness of documentation and my own understanding)

The JS code produced still accepts 1 + “99” and returns 199. Where would this inference and aforementioned checks be taking place in the edit/run/debug cycle?

If I am looking for a compiler-time check on things like this like I might get is Sacla:

scala> val addInt = (x: Int, y:Int) => x + y
     | 
val addInt: (Int, Int) => Int = $Lambda$2148/0x00000001008d1040@7126e26

scala> addInt(1, "99")
                 ^
       error: type mismatch;
        found   : String("99")
        required: Int

… what would I do? Am I thinking about this all wrong or using the wrong tools? It might be something silly because I’ve never “mixed” a dynamic and static language before.

My goal is to be able to writer cleaner code with some sanity checks from a type system in our Vue apps, am I barking up the wrong tree?

Hi sweeney-th,
it’s important to note that JavaScript (the resulting artifacts of the rescript compiler and in general) has no types like you would use them in a typed language. If you want to check for a specific type in js you would need to call typeof on your function args inside of your functions implementation and handle everything yourself.
Therefore when you compile your rescript code to js, types are “erased” during compilation.

On the other hand rescript has awesome inference and is statically typed. (This is what the docs refer to.) Usually the rescript compiler know the types of your values without needing hints. (type annotations).
This means the rescript compiler will error on such code, even without type annotations:

let add = (x, y) => x + y
let compilerErro4 = add(1, 1.0) // add only takes Ines because + only takes ints

Calling the compiled add function from js code with a string will produce a runtime error.

This concludes: you only have type-safety in your reason code.

Hi, woeps,

Thanks for getting back to me so fast. I am glad I am not losing my mind. It makes perfect sense that the ReScript compiler is going to catch that and that JS isn’t - I think I had initially assumed the compiler would make JS that would catch you, not catch you itself which makes more sense now that you point it out - thanks for that :).

It seems what I’m missing is more of a workflow issue.

I think I put to much faith in this guide which in my understanding is passing arguments to post compiled JS, with all of it’s typical type issues and gaining no benefits of ReScript’s type checks. To make this work in this context, I would need to make sure that value is calculated before we get to JS.

So, the typical way to use ReScript would be to have all the logic I am attempting to add type safety to simply be conducted in ReScript to get the compiler errors when I expected them before using the resultant JS later knowing it was generated by a typesafe system. Do best practices for integrating with a system like Vue exist? I will see if I can make this work if not.

It would be in the compile step: edit -> compile -> run -> debug

Does this mean I can use this strategy to check things that are being passed to my components as in the sandbox I provided?

Thank you! I should have guessed that sooner - I got mixed up on what was coming from the compiler and what was coming from the JS produced (in this case, nothing lol)

1 Like

I got version that does what I want working, see here if curious.