Can't use locally abstract types with @react.component?

This generates a compile error Fatal error: exception Invalid_argument("react.component calls can only be on function definitions or component wrappers (forwardRef, memo).") Why isn’t this supported? Is there a workaround?

@react.component
let make = (type apple, ~a as _: apple) => <div />

It seems nearly identical to this, which does compile…

@react.component
let make = (~a as _: 'apple) => <div />

My real scenario is more complex - I’m using a first-class module - and I don’t know how to get it to compile without using locally abstract types.

module type QuizType = {
  type t
  type configuration
  let make: configuration => t
  let run: t => unit
}

@react.component
let make = (
  type configuration,
  ~quiz as _: module(QuizType with type configuration = configuration),
  ~configuration as _: configuration,
) => <div />

The following component can be used only if configuration and QuizType's configuration are the same.

module type QuizType = {
  type t
  type configuration
  let make: configuration => t
  let run: t => unit
}

@react.component
let make = (
  ~quiz as _: module(QuizType with type configuration = 'configuration),
  ~configuration as _: 'configuration,
) => <div />

Is this what you require?

Yes, but that won’t compile as soon as you try to unpack the module. The error is “The type of this packed module contains variables.” See “generic” modules - General - ReScript Forum (rescript-lang.org)

@react.component
let make = (
  ~quiz: module(QuizType with type configuration = 'configuration),
  ~configuration as _: 'configuration,
) => {
  let module(Q) = quiz
  let _q = Q.make(configuration)
  <div />
}

For a React component, which needs to be a module anyway, I recommend using a normal functor rather than a function which takes a first-class module. To me it looks like the @react.component PPX doesn’t understand locally abstract types as parameters.

Here is the functor approach. Works pretty well.

module type QuizType = {
  type t
  type configuration
  let make: configuration => t
  let run: t => unit
}

module type QuizComponentType = {
  type configuration

  @react.component
  let make: (~configuration: configuration) => React.element
}

module type MakeType = (P: QuizType) =>
(QuizComponentType with type configuration := P.configuration)

module Make: MakeType = (P: QuizType) => {
  @react.component
  let make = (~configuration: P.configuration) => {
    configuration->P.make->P.run
    <div />
  }
}

module IntQuiz: QuizType with type configuration = int = {
  type t = int
  type configuration = int
  let make = _ => 1
  let run = _ => ()
}

module IntQuizComponent = Make(IntQuiz)

let make = () => <div> <IntQuizComponent configuration=3 /> </div>
2 Likes