How to handle discriminated union bindings?

I’m creating my first “serious” project in Rescript using React + NextJS and it’s has been a great experience.
I know there are already projects that created bidings for NextJS in github. But I’m creating my own bidings as a way of learning.

So I was typing the <Link> component and it has this href property that has two possible types:

image

Following the example they gave for the case of being an “object”, in my project the typing was like this:

image

That’s more or less where I stopped. I’ve tried looking up material about it online, but the examples are all so complex that they seem to run away from a “simpler” case like this one. And I know that in practice, I’ll end up passing a string to the href in 99% of cases (that’s how they typed it in github projects). However, it is still a learning opportunity and can be used when I deal with more complex data.

I have a vague idea that I should use a switch here. But I have no idea how.

If it serves as a reference, the declaration of the component looks like this:

image

In v11 of ReScript (about to be released, in RC right now) this is solved via “untagged variants” (blog post: Better interop with customizable variants | ReScript Blog). Untagged variants let you mix different types together in a way that still conforms to the ReScript type system.

For your example, your definition would look something like this:

type hrefConfig = {
  pathname: string,
  query?: Dict.t<string>,
}

// Constructor names can be called anything, it's the payload that matters
@unboxed type href = Path(string) | Config(hrefConfig)

<Link href={Path("/page1")}>

// or

<Link href={Config({pathname: "/page1"})}>

This will compile to a string or an object in JavaScript.

5 Likes

So simple right now ^-^

Thanks.

1 Like

Worth noting is that if you’re using a string 98% of the time it might make sense to make the binding take a string so it feels ergonomic, and then do an additional biniding to Link like LinkWithHrefConfig where href is the object, and use the two as needed. Binding to the same component several times with different params costs nothing in terms of bundle size, they’ll refer to the same component in runtime anyway.

4 Likes

Nitpick: string | Record is not what they call discriminated unions in TS, so I think the title of the OP is misleading.

2 Likes