Daniel Ortiz
Daniel Ortiz

Reputation: 306

Disjoint Union in Flow not being "disjointed"

I am having some issues trying to use the "disjoint unions" created by flow. Apparently flow tries to mash them together, but I still don't actually know why.

type Single = {|
  type: 'single',
  value?: string,
  onChange: (value?: string) => void,            // line:47
|};

type Multiple = {|
  type: 'multiple',
  value?: string[],
  onChange: (value: string[]) => void,           // line:53
|}
type Props = Single | Multiple;

Having the flow definitions from above, I should be able to do this without any error, right?

// (Yes, it is react)
function someFunc(receivedValues?: string[]) {
  // ...
  // value, type and onChange were pulled from "props: Props"

    if (type === 'single') {
      if (receivedValues && receivedValues.length) {
        onChange(receivedValues[0]);                     // line:110
      }
    } else if (type === 'multiple') {
      if (receivedValues && receivedValues.length) {
        const casted: string[] = value && value.length ? value : [];
        onChange(casted.concat(receivedValues));         // line:115
      }
    }

  //...
}

But flow throws me multiple errors:

Error: file.jsx:110
110:   onChange(receivedValues[0]);
                ^^^^^^^^^^^^^^^ string. This type is incompatible with the expected param type of
 53:   onChange: (value: string[]) => void,
                         ^^^^^^^^ array type

Error: file.jsx:114
114:   const casted: string[] = value && value.length ? value : [];
                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ string. This type is incompatible with
114:   const casted: string[] = value && value.length ? value : [];
                     ^^^^^^^^^^ array type

Error: file.jsx:115
115:   onChange(casted.concat(receivedValues));
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ array type. This type is incompatible with the expected param type of
 47:   onChange: (value?: string) => void,
                          ^^^^^^^ string

For what I understand it is just merging Single and Multiple, and it is not making the selection of one of them. Does anyone have any idea on how to fix this?

Upvotes: 2

Views: 407

Answers (1)

loganfsmyth
loganfsmyth

Reputation: 161677

Since you've pulled the values off of the object already, checking type has no effect on narrowing down the type of value and onChange. Your code will work if you work with the object directly:

function someFunc(receivedValues?: string[]) {
  var obj: Props = { type: 'single', onChange: () => {} };

  if (obj.type === 'single') {
    if (receivedValues && receivedValues.length) {
      obj.onChange(receivedValues[0]);
    }
  } else if (obj.type === 'multiple') {
    if (receivedValues && receivedValues.length) {
      const casted: string[] = obj.value && obj.value.length ? obj.value : [];
      obj.onChange(casted.concat(receivedValues));
    }
  }
}

(On Flow playground)

or only destructure the values off the object after refining the type.

function someFunc(receivedValues?: string[]) {
  var obj: Props = { type: 'single', onChange: () => {} };

  if (obj.type === 'single') {
    const { onChange } = obj;
    if (receivedValues && receivedValues.length) {
      onChange(receivedValues[0]);
    }
  } else if (obj.type === 'multiple') {
    const { onChange, value } = obj;
    if (receivedValues && receivedValues.length) {
      const casted: string[] = value && value.length ? value : [];
      onChange(casted.concat(receivedValues));
    }
  }
}

(On Flow playground)

Upvotes: 3

Related Questions