Reputation: 3909
I'm trying to implement a React component that contains a list of options and shows their id
and name
. I want this component to be reusable so I define an interface Option
to ensure the required fields are always provided.
And here comes the issue: if I pass any type with more fields than those 2 { id, name, /* anything */}
, Flow complains. Is it not possible to use interfaces in Flow like this?
Here's the minimal relevant code:
interface Option {
id: string,
name: string
}
const List = (options: Option[]) => {
options.forEach(o => null)
}
type ImplementsOption = {
id: string,
name: string,
description: string
}
const plans: ImplementsOption[] = []
List(plans)
Error:
Cannot call
List
withplans
bound tooptions
because propertydescription
is missing inOption
1 but exists inImplementsOption
[2] in array element.
Trying with casting:
List((plans: Option[]))
And also with classes:
class ComplexOption implements Option {
id: string
name: string
}
const complexOptions: ComplexOption[] = []
List(complexOptions)
Nothing seems to work!
There is a playground with all these snippets already.
Upvotes: 0
Views: 211
Reputation: 828
Imagine we had a list of ImplementsOption
: [{ id: 'id', name: 'name', description: 'description' }, ...]
. Now we pass it into the List
function, which has the signature Option[] => void
. This is totally valid from the point of view of List
since ImplementOption
is a supertype of Option
. However, there is no guarantee in the List
function that it won't modify the list that is passed in. Thus, the function could add an element of type Option
to the list, which would be valid for a Option[]
but invalid for a ImplementsOption[]
.
To fix this, you can type plans
as a $ReadOnlyArray<Option>
, which tells Flow that the List
function will not modify the elements (try flow).
Reference issues #4425, #4483, or #5206 for more information.
Upvotes: 2