Hi L33! I’ll try to answer you though I have zero knowledge of AGS.
Be careful, you’re mixing record and object type declarations, you probably want to define records here since, they’re overall easier to use and provide better error messages (and that’s what JSX components expect though I’m not sure you’re using JSX here).
What do you mean by unwrapping? I guess you mean you want them to be unboxed/untagged right? This is not possible if they’re both objects or of unknown/opaque types. You can check the docs about untagged variants if you want to know more about them.
If you’re only producing those objects that are then consumed on the JS side, you can fix it in two different ways:
- the easiest solution is just to define different bindings for each type of child
module Box = {
type t
module WithLabelChildren = {
type props = {
...baseProps,
child: Label.t,
children: array<Label.t>,
}
@val @scope("Widget") external make: props => t = "Box"
}
module WithBoxChildren = {
type props = {
...baseProps,
child: t,
children: array<t>,
}
@val @scope("Widget") external make: props => t = "Box"
}
}
The problem here is that you’ll probably need to do that for more than two types so it can’t get cumbersome and you can’t have an array of mixed types.
In order to circumvent those issues you can use an opaque type instead (playground link):
type widget
type baseProps = {name: string}
module Label = {
type t = {widgetType: string}
external toWidget: t => widget = "%identity"
type props = {
...baseProps,
label?: string,
}
@val @scope("Widget") external make: props => t = "Label"
}
module Box = {
type t
external toWidget: t => widget = "%identity"
type props = {
...baseProps,
child: widget,
children: array<widget>,
}
@val @scope("Widget") external make: props => t = "Box"
}
If you want to avoid having to explicitly cast to widget every time, you can also a phantom type parameter for widget, but to be honest that opens another can of worms in terms of typing, not sure it’s actually worth the tradeoff (playground link):
type widget<'kind>
type baseProps = {name: string}
module Label = {
@get external widgetType: widget<[> #Label]> => string = "widgetType"
type props = {
...baseProps,
label?: string,
}
@val @scope("Widget") external make: props => widget<[> #Label]> = "Label"
}
module Box = {
@get external boxType: widget<[> #Box]> => string = "boxType"
type props = {
...baseProps,
child?: widget<[#Box | #Label]>,
children?: array<widget<[#Box | #Label]>>,
}
@val @scope("Widget") external make: props => widget<[> #Box]> = "Box"
}
let labelWidget = Label.make({name: "label"})
let widgetType = Label.widgetType(labelWidget)
let boxWidget = Box.make({
name: "box",
children: [labelWidget],
})
let boxWidget2Type = Box.make({
name: "box2",
children: [labelWidget, boxWidget],
})->Box.boxType