Simon Long
Simon Long

Reputation: 1440

Flow complains about type incompatibility even though I check type first

I have written a React dropdown component which I can supply either of the following to :

My simple flow types look as follows:

type DropdownMenuItemType = DropdownMenuIconAndTextType | string;

type DropdownMenuIconAndTextType ={
    text: string,
    icon?: React$Element<React$ElementType>;
}

Previous versions of the component only supports strings. The addition of an element to support text and an icon is a new feature request which I am in the process of implementing. I don't want any breaking changes for my existing users.

Therefore within my component I try to attempt to convert any string supplied and wrap it in a DropdownMenuIconAndTextType so everything ends up as this type. Items that are already DropdownMenuIconAndTextType just remain so.

let Array<DropdownMenuItemType> originalItems = 
let Array<DropdownMenuIconAndTextType> convertedItems = [];
{'English', 'French', 'German', {text: 'Dutch', icon : <SomeIcon />}};
     items.forEach( (currItem: DropdownMenuItemType) => {
           if(typeof currItem === DropdownMenuIconAndTextType){
               convertedItems.push(currItem);
           }
           else {
               convertedItems.push({text: currItem.toString()});
           }

}); 

However flow has one error with :

  if(typeof currItem === DropdownMenuIconAndTextType){
               convertedItems.push(currItem);
  }

and it says that currItem could still be a string and is incompatible with convertedItems despite it being type checked as DropdownMenuIconAndTextType.

What do I need to do to satisfy flow in this scenario ? Thanks in advance.

Upvotes: 1

Views: 143

Answers (1)

josephjnk
josephjnk

Reputation: 348

I believe you're mixing up the distinction between Flow's type code and JS code.

Inside of type signatures, typeof returns the type of a literal value, as described here. In the JS code that exists at runtime, such as in your if statement, typeof will just tell you whether something is a string, object, etc., as described here. So the left side of your conditional operator will evaluate to either "string", or "object", not to the actual Flow type of the variable.

On the right side of your conditional, you have the Flow type DropdownMenuIconAndTextType, which only exists at type-checking time, not at runtime. I'm kind of surprised that Flow doesn't give you an error because of that.

Try something like this:

  if(typeof currItem !== 'string'){
               convertedItems.push(currItem);
  }

This will check whether the value that exists at runtime is a string or an object, which should work with Flow's type refinements.

Upvotes: 2

Related Questions