How to specify a type that covers all subtypes?

Hi there,

I have a select component that accepts a type roughly similar to the below

type selectOption = {
  id: string,
  label: string
}
type selectOptions = array<selectOption>

and where I want to use this select I have a type which describes the only options the server API supports

type frequency = [#weekly | #daily]

I’m wondering if it’s possible to alter the selectOptions type in some way to constrain the possible selectOption by the frequency type, I’ve tried the following:

type selectOption<'a> = {
  id: string,
  label: string
}
type selectOptions<'a> = array<selectOption<'a>>

Which does work, but allows any type for id, it’s necessary for the select component I’m using that the id eventually ends up as a string (as it results in html tags).

I’m aware of this operator :> (although I’m not sure what it’s called?) but that only works on values, e.g.

let myFreq = #weekly
let str: string = myFreq :> string

So my question is, is there a way to specify a type for id in selectOption that allows “any type that is a subtype of string”?

Thanks :pray:

Would this work for you? Or did I misunderstand the requirement?

You can even add one more case to your pattern match for handling any other string:

let handleProps = (selected: selectOption) => {
  switch selected.id {
  | #daily => Js.log("daily")
  | #weekly => Js.log("weekly")
  | #monthly => Js.log("monthly")
  | _any => Js.log("handle any other string")
  }
}

Ah right sorry, here’s a link that demonstrates where I’m getting stuck: code example

I could add a function to map from the id's type to string like this, this seems a bit unnecessary as I may as well just specify the id as string.

Ideally the selectOption.id type specifies all types that are a subset of string (as far as I can tell polymorphic variants are subtypes of string in rescript). Is this possible?

The reason this would be beneficial is because we’re using atdgen generated types (these polymorphic variants) together with reform which then allows us to make it impossible to specify options of the select that don’t conform to the atdgen types at compile time.

A couple ways you could approach this. The links are for the rescript playground.

Option 1: This is a simple fix of the code example you show above – move the frequency type up so that you can refer to it in the Select module. You need to specify to Select.make that the value you’re passing in has an id that can in fact be coerced to string. You can see that by specifying the type of the options argument explicitly in Select.make.

Option 2: like option 1, but you don’t need the type variable because you’re specifying that the id is type frequency.

Option 3: This one is a bit different and it may not work for you as I don’t know your full needs. But judging from the examples you posted, the selectOption just needs the id and a label and it looks like the label is always based on the ID, so no need to store both. Rather of making a selectOption record type, instead make a Frequency.t type that is your poly variant, and add a function so it knows how to generate a label given a value of that type. I think it simplifies things nicely, but again, you could have a good reason for using the record (like more fields or whatever and you’ve just shown us a simplified example, for instance.)


By the way :> is the type coercion operator. That link is for the syntax lookup tool. It’s a neat tool!!