Even more JSX customization

While experimenting with mithriljs, I was curious if I could use it in rescript (with jsx support) as well.

So I started a new project, added a JSX module and implemented all necessary types and functions.

Example of a mithril component in js:

const Component = (vnode) => {
  console.log(vnode.attrs.message);

  return {
    view: vnode => <div>{vnode.attrs.message}</div>
  };
};

I encountered some problems, but its difficult to explain or to show code because I tried a lot. But here are some of them:

  • (By default) rescript expects components to be functions with a single parameter (props) which will return jsx.
    • With some type gymnastics, I can write my components, but I can’t force to make all components have this shape.
    • @jsx.component / @jsx.componentWithProps don’t work on different shapes.
  • Jsx.elements can’t have a type parameter, so it’s not easy to type the vnode parameter to have the same type as in the view function.
    (type rec element<'props> = {view: 'props => element<'props>})

Here is my current rescript solution. It compiles, but

  • I could also define components, which just return jsx and they will crash at runtime.
  • I have to specify the types of my make functions explicitly or I need these helper functions.
  • I need an empty record type for components without props. Otherwise I get this error: “Empty record literal {} should be type annotated or used in a record context.”.
// M.res
type noProps = {}
type vnode<'props> = {attrs: 'props}
type comp<'props> = {view: vnode<'props> => Jsx.element}

let component = (options: vnode<noProps> => comp<noProps>): (noProps => Jsx.element) => {
  Obj.magic(options)
}

let componentP = (options: vnode<'props> => comp<'props>): ('props => Jsx.element) => {
  Obj.magic(options)
}
// Counter.res
type props = {initialCount: int}

let make = M.componentP(vnode => {
  let count = ref(vnode.attrs.initialCount)

  {
    view: _vnode => {
      <div>
        <button onClick={_ => count.contents = count.contents + 1}>
          {Jsx.string("click me")}
        </button>
        <span> {Jsx.int(count.contents)} </span>
      </div>
    },
  }
})

TLDR;

It’s just an experiment. I don’t know, if I want to use mithril in any project. But I had similar problems when I was implementing some web component stuff. So my question:

What is the future of JSX in rescript?
Do you think it is feature complete?
Are the improvements in mind (looking at custom data attributes)?

1 Like