Strongly-typed React form events with gentype

Part of the challenge of using ReScript is knowing when something is easy/possible/works-by-default or requires some extra effort. I search and search and search for what I assume must be baked-in but come up empty. I’m trying to use GenType with a React library and a component takes the parameter below. In TypeScript this all works just fine. The event handler is strongly typed and you can easily access all the properties.

onChange?: (event?: React.ChangeEvent<HTMLInputElement>, newValue?: string) => void;

function myEventHandler(i: React.ChangeEvent<HTMLInputElement>) {
  console.log("Placeholder is %s", i.currentTarget.placeholder);
  console.log("Value is %s", i.currentTarget.value);
}

How can I define this event handler in ReScript so GenType won’t complain?

// Cannot find './ReactEvent.gen' or its corresponding type declarations
~onChange: (ReactEvent.Form.t, Js.undefined<string>) => unit=?,

// Cannot find './ReactEvent.gen' or its corresponding type declarations
~onChange: (ReactEvent.Synthetic.t, Js.undefined<string>) => unit=?,

Even if I could get this to work, I don’t know how to safely work with this event in ReScript. For example, the best I can do below is convert the input event as a ReactEvent.Form.t which is really generic; doesn’t know this is a text input control.

<input
  onChange={i => {
    let inputControl = i->ReactEvent.Form.currentTarget
    Js.Console.log2("The value is %s", inputControl["value"])
  }}
/>
  • What type on the ReScript side is best fit for React.ChangeEvent<HTMLInputElement>?
  • How can I make it work with GenType?
  • Has someone already written a wrapper to provide more type-safe access to React form events?
  • I could make this work by writing my own wrapper that creates a React.ChangeEvent<HTMLInputElement> and gentype import it. This really isn’t hard at all. But I don’t want to do this if such a thing is already built-in and I just haven’t found it.

Yeah, there’s no such type-safe event handling in ReScript as far as I know. I’d be interested to see what you mean by the wrapper. From what I can tell it would be possible to do a safe design but would require a lot of drudge work. I came up with a design recently: Proposal: type-safe event listeners

By a “wrapper” I mean something like this. Cumbersome to write these things. I don’t think GenType can do something like send or constructors and validate parameters, so to get some type safety I have to write wrapper functions for constructors and everything and GenType import them.

Here is the .tsx file.

export type ChangeEventHTMLInputElement = React.ChangeEvent<HTMLInputElement>;

// All my wrapper functions here
export const changeEventHTMLInputElement = {
  preventDefault: (i: React.ChangeEvent<HTMLInputElement>) =>
    i.preventDefault(),
  value: (i: React.ChangeEvent<HTMLInputElement>) => i.currentTarget.value,
};

Here is the ReScript side…

module ChangeEventHTMLInput = {
  @genType.import(("./fluentUI", "ChangeEventHTMLInputElement"))
  type t

  @genType.import(("./fluentUI", "changeEventHTMLInputElement.preventDefault"))
  external preventDefault: t => unit =
    "changeEventHTMLInputElement_preventDefault"

  @genType.import(("./fluentUI", "changeEventHTMLInputElement.value"))
  external value: t => string = "changeEventHTMLInputElement_value"
}

And now I will delete all this code. Didn’t end up needing it.

1 Like