Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Possible to iterate over two union arrays with flow

I have a function foo that takes a union of two arrays, and the only thing it does is loop over the array, but with this I get flowtype errors, that some properties are missing in the array. Is it not possible to do this? I'm not using any properties at all. They are both arrays, so flow should know that they are iterable.

A live example in the flow editor

type Handle = {|
  +articleId: string,
  +type: 'handle',
  +accessories: Array<string>,
  +positionInfo: string,
|};

type Leg = {|
  +articleId: string,
  +type: 'leg',
|};

type Entity = Handle | Leg;

function foo(entities: Array<Handle> | Array<Leg>) {
  entities.forEach(() => {})
}
like image 928
stinaq Avatar asked Jul 11 '18 07:07

stinaq


1 Answers

You can type the array as containing Entity objects (e.g. Array<Entity>), which you can later refine. Alternatively, you could type the input as Array<Handle | Leg>, but since you already have the Entity type defined, we should use that.

(Try)

// I made these types writeable and non-exact since it
// doesn't seem to matter for the sake of this question.
// You can make them read-only and exact in your code if
// you want (and I personally would unless I had good
// reason not to).

type Handle = {
  type: 'handle',
  articleId: string,
  accessories: Array<string>,
  positionInfo: string,
}

type Leg = {
  type: 'leg',
  articleId: string,
}

type Entity = Handle | Leg;

function foo(entities: Array<Entity>) {
  entities.forEach(entity => {
    // At this point, flow knows that our `entity` variable
    // either contains a Handle or a Leg (from the above
    // definition of the `Entity` type.) We can use a
    // refinement to figure out which one it is:
    if (entity.type === 'leg') {
      const {type, articleId} = entity
      console.log("A leg", type, articleId) 
    } else if (entity.type === 'handle') {
      const {type, articleId, positionInfo} = entity
      console.log("A handle", type, articleId, positionInfo) 
    } else {
      // We can even assert that we covered all possible
      // cases by asserting that the `entity` value has
      // no remaining union cases and is therefore empty
      (entity: empty) 
    }
  })
}

const entitiesExample = [
  {articleId: 'blah', type: 'leg'},
  {articleId: 'toot', type: 'handle', accessories: [], positionInfo: 'bar'}
]

foo(entitiesExample)

Sidenote: I noticed flow had trouble refining the types if I used == check on the type property instead of the above ===. If anyone has any idea what's going on there, I'd love to know, since my intuition is that == should work fine.

like image 77
James Kraus Avatar answered Oct 31 '22 23:10

James Kraus