ADT Bypass TypeChecking, WOW!

type action =
  | SetThing(int)

type state = {thing: int}

let reducer = (state, action) => {
  switch action {
  | SetThing(thing) => {state.thing: thing}
  }
}

let (state, dispatch) = React.useReducer(
    reducer,
    {thing: 0},
  )

let handleChange = event => {
    ReactEvent.Form.currentTarget(event)["value"]->SetThing->dispatch // this will work but will blow up in the use, here we send value from form which is "text": string to SetThing which need int.
}

The problem is, when I use “thing” in reducer, I thought it was already int, but not, it’s still string, so, my Array filter failed.

My current workaround is to parse input to int first of course.

switch ReactEvent.Form.currentTarget(event)["value"] |> Belt.Int.fromString {
     | Some(value) => value->SetThing->dispatch
     | None => ()
     }
    }

I just wondering, how is it possible SetThing which require (thing: int) passed a string value and it’s compile ?

That’s because
ReactEvent.Form.currentTarget(event)["value"] is of type 'a, you have to take care of the type yourself. I agree it can be a bit dangerous.

2 Likes

Oh it’s type is 'a ? I thought it was string.

Honestly I can’t check type from modules @rescript/react

What I know from doc is just

* `React`: Bindings to React
* `ReactDOM`: Bindings to the ReactDOM
* `ReactDOMServer`: Bindings to the ReactDOMServer
* `ReactEvent`: Bindings to React's synthetic events
* `ReactDOMStyle`: Bindings to the inline style API
* `RescriptReactRouter`: A simple, yet fully featured router with minimal memory allocations

This is one of the reasons why I think just using the raw object accessors is not the endgame solution. What’s the use of a robust type system when you have to be cautious in spite of it?

As an aside, I find it rather unfortunate that a function that is not generic accepts anything for input. I mean, surely the typechecker cannot prove that the value is as narrow as the SetThing constructor expects?

2 Likes

You can check the types directly: https://github.com/reasonml/reason-react/blob/v0.9.1/src/ReactEvent.rei#L174

[@bs.get] external currentTarget: t => Js.t({..}) = "currentTarget";

(Actually your editor plugin should also show this type on hover.)

The return type Js.t({..}) means ‘can be literally any JavaScript object’. So you can dereference any property on the object and it won’t be typechecked statically, but may error at runtime. Basically, ReScript has some type safety issues that should be kept in mind, and this is one of the big ones.