operational-ui icon indicating copy to clipboard operation
operational-ui copied to clipboard

Better type definition for `Select`

Open fabien0102 opened this issue 6 years ago • 8 comments

Problem

The type definition in the onChange part of Select have too many options and don't infer correctly from the options provided to the component.

Snippet

<Select
      options={[{ label: "option1", value: "option 1" }]}
      onChange={value => {
        console.log(value); // This should be `string` only
      }}
    />

Codesandbox

Edit vk9n69v00

fabien0102 avatar Nov 16 '18 13:11 fabien0102

This is interesting. The value passed in the onChange callback seems like it should be any in order to handle cases like:

<Select
  options={[
  {
    label: "John",
    value: -10,
  },
  {
    label: "Joey",
    value: { holy: "Yes" },
  },
  {
    label: "Tupac",
    value: "true",
  },
  {
    label: "Chandler",
    value: 10,
  },
]} />

We have numbers in some cases, objects in others, and strings in other cases and we don't really know which one can/will be chosen.

I thought about generics but I'm not sure we can get type safety here. Ideas?

TejasQ avatar Nov 21 '18 00:11 TejasQ

ping @fabien0102

TejasQ avatar Dec 06 '18 09:12 TejasQ

I think that we should be able to infer from options even in this extreme case -> number | {holy: string} | string | number.

After it's not an easy thing to fix 😅

fabien0102 avatar Dec 06 '18 13:12 fabien0102

Hmmm. But options is an array where value can be anything, right? In my example,

{
  label: React.ReactElement<any> | string
  value: any // what would you have here?
}

value in this case can be anything. We do not enforce a type of value. Should we? We might be able to make value T where all values are either a string or an object of a certain shape. That might be safer actually.

TejasQ avatar Dec 06 '18 13:12 TejasQ

We just want to infer, so T should fit this usecase. T will be used just to have a cross type reference, not for restrict the API 😉

fabien0102 avatar Dec 06 '18 13:12 fabien0102

You mean this?

interface SelectProps<T> {
  // ... other props
  options: Array<{ label: React.ReactElement<any> | string, value: T }>
}

In this case, each option in options' value has to be the same type. This example will not work, right? Unless I missed something...

TejasQ avatar Dec 06 '18 13:12 TejasQ

Link to playground

True! I don't know how to describe this with typescript ^^

fabien0102 avatar Dec 06 '18 13:12 fabien0102