Switching on optional field vs optional value

I’m switching an an optional value (settings). Settings has some fields which also have optional values. In my desired condition I want to make sure that settings is Some but want any optional fields to be destructured, but not have to be Some. I thought this is how ReScript has always been but maybe I’m missing something here. Code time -

In this code I reach my first case as expected, when choices is defined but the defaultLabel doesn’t have to be.

      let defaultLabel = switch settings {
      | Some({defaultLabel: Some(defaultLabel)}) => defaultLabel
      | _ => "Date"
      }

      let component = switch settings {
      | Some({choices}) =>
        <ToolbarDropdown
          choices
          ?size
          toggle={choice => setSelected(_ => choice->Some)}
          label={selected
          ->Option.flatMap(i => choices->Choice.find(i))
          ->Option.mapOr(defaultLabel, i => i.label)}
          renderItem={({choice}) => choice.label->str}
        />
      | _ => React.null
      }

In this code I do not reach my first case as expected.

      let component = switch settings {
      | Some({choices, defaultLabel}) =>
        <ToolbarDropdown
          choices
          ?size
          toggle={choice => setSelected(_ => choice->Some)}
          label={selected
          ->Option.flatMap(i => choices->Choice.find(i))
          ->Option.mapOr(defaultLabel->Option.getOr("Value"), i => i.label)}
          renderItem={({choice}) => choice.label->str}
        />
      | _ => React.null
      }

Shouldn’t my second switch statement work?

Can you show the type of settings?

For sure:

type choice<'id> = {
  label: string,
  id: 'id,
}

type getCompareValue<'item, 'compareValue> = 'item => 'compareValue

module FilterTypes = {
  type settings<'item, 'compareValue, 'id> = {
    getCompareValue: getCompareValue<'item, 'compareValue>,
    initialValue?: option<choice<'id>>,
    choices: array<choice<'id>>,
    defaultLabel?: option<string>,
  }

  type getCompareValue<'item, 'compareValue> = getCompareValue<'item, 'compareValue>

  type hookValue<'item, 'compareValue, 'id> = {
    reset: unit => unit,
    selected: option<choice<'id>>,
    component: React.element,
    settings: option<settings<'item, 'compareValue, 'id>>,
  }
}

OK I think I understand, the issue is here:

  type settings<'item, 'compareValue, 'id> = {
    getCompareValue: getCompareValue<'item, 'compareValue>,
    initialValue?: option<choice<'id>>, // optional field with an optional value
    choices: array<choice<'id>>,
    defaultLabel?: option<string>, // optional field with an optional value
  }

What you actually want I think is this:

type settings<'item, 'compareValue, 'id> = {
    getCompareValue: getCompareValue<'item, 'compareValue>,
    initialValue?: choice<'id>, // optional field 
    choices: array<choice<'id>>,
    defaultLabel?: string, // optional field 
  }

When you do this:

let component = switch settings {
      | Some({choices, defaultLabel}) =>

You’re actually checking is defaultLabel is set in this record, even if it’s set as None.

Or maybe I didn’t understand your issue, then it’d be interesting to come up with a working example on the playground.

2 Likes

Ah, okay. So to accomplish what I want I’ll need to make it so the field isn’t optional, or I’ll just have to handle it the way I am now. That makes sense. I guess I didn’t expect the switch case to skip if the optional field wasn’t there. Thanks for the clarification!

2 Likes