Bartłomiej Gładys
Bartłomiej Gładys

Reputation: 4615

Can't set type to Array<string> in flow-js

I have following error from flow in one before the last line of function getRowData:

message: 'Cannot call getRow with el.arr bound to data because: Either property arr of unknown type [1] is incompatible with array type [2]. Or property arr of unknown type [1] is incompatible with string [3]. (References: [1] [2] [3])' at: '60,44' source: 'flow' code: 'undefined'

This error is disappearing only if I change Array<string> in type UserProps to Array<mixed>, but I'd like to stay with the array of strings.

I don't understand why I get this error. I explicitly set data.audio and data.chat to Array<string>. Any help will be highly appreciated!

type UserProps = {
  name: string,
  audio: Array<string>,
  chat: Array<string>,
  enableChat: boolean,
  enableAudio: boolean,
  status: string,
};

const getRow = (key, data: string[] | string, style = {}) => {
  const text = Array.isArray(data) ? data.join(", ") : data;
  return (
    <TableRowColumn
      key={`row-${key}`}
      style={{ whiteSpace: "pre-wrap", ...style }}
    >
      {text}
    </TableRowColumn>
  );
};

const getRowData = (data: UserProps) =>
  [
    data.name,
    data.status,
    {
      arr: data.audio,
      style: { color: data.enableAudio ? "green" : "red" }
    },
    { arr: data.chat, style: { color: data.enableChat ? "green" : "red" } }
  ].map(
    (el, i) =>
      el && el.arr && el.style ? getRow(i, el.arr, el.style) : getRow(i, el) // error in el.arr !!!!!
  );

Upvotes: 0

Views: 1324

Answers (2)

Aleksey L.
Aleksey L.

Reputation: 37978

Error is a bit confusing, but the problem is that flow fails to refine the el type. What you can do to is slightly change the refinement:

typeof el === 'string' ? getRow(i, el) : getRow(i, el.arr, el.style)

You can try it here

Upvotes: 1

Dave Meehan
Dave Meehan

Reputation: 3201

getDataRow does not have an annotated return type, so its implied by the return value, which is an array consisting of string, string, object, object, which is then passed through map (which returns the same type as the input). el in the map callback is therefore string | object.

Flow can't determine if arr is a property of string or object. It could be undefined (I've found problems with flow's interpretation of compound logical tests like you have in el && el.arr && el.style. If you spell them out as if statements they work as expected, but logical && and || seems problematic).

As a result, flow thinks el.arr is not compatible with the second argument of getRow which requires string[] | string.

You'd need to determine the type of el before accessing the properties for flow to be happy. Testing for the presence of members arr and style does not do that (although you might want to look at Disjoint Unions as a way of doing so, if your types are malleable, but I think that's wouldn't work in this case because you have no option but to use the stock definition of string).

As a side note, whilst the shorthand array syntax ({type}[]) can be convenient, it can be confusing with optional or maybe values before you have less scope for where the ? goes. As a result, I have opted to use the longhand Array<{type}> syntax. There's more in the docs on the subject.

Upvotes: 1

Related Questions