import React from 'react'
type BaseProps = {
hasFocus: boolean,
}
class Entity {
changeContent(content: null | string, selection: null | number): void {
}
}
type Props = BaseProps & {
entity: Entity,
}
export default class View extends React.Component {
props: Props
onChange = (value: string) => {
this.props.entity.changeContent('blah', '12')
}
}
Flow reports this error:
20: this.props.entity.changeContent('blah', '12')
^ property `entity`. Property cannot be accessed on any member of intersection type
20: this.props.entity.changeContent('blah', '12')
^ intersection
... instead of this error:
20: this.props.entity.changeContent('blah', '12')
^ string. This type is incompatible with
8: changeContent(content: null | string, selection: null | number): void {
^ union: null | number
This is expected...you should use an exact type and the spread operator for this example:
//...
type BaseProps = {|
hasFocus: boolean,
|}
//...
type Props = {
entity: Entity,
...BaseProps
}
//...
An object literal type only means that it _at least_ has the the hasFocus: boolean, but other properties could be present. The same goes for type Props...
type BaseProps = {
hasFocus: boolean,
}
Since other properties could be present, it's feasible for BaseProps to have a entity value, thus making the type of entity in Props unknown (and unsafe) for an intersection.
Thank you. This is precisely how I ended up fixing the issue, but still:
I really wonder when this intersection type is really useful. Looks like it's troublesome to use right !
Yeah, exact types are much easier to reason about!
If I鈥檓 reading this right, looks like we understand this now and the issue can be closed. If that鈥檚 wrong, let me know and I鈥檒l reopen.
As of Flow 0.55, this gets a better error message and shows that I think my intuition about this was wrong.
Here's the error that's shown:
19: this.props.entity.changeContent('blah', '12')
^ property `entity`. Property cannot be accessed on any member of intersection type
19: this.props.entity.changeContent('blah', '12')
^ intersection
Member 1:
12: type Props = BaseProps & {
^ BasePropsError:
19: this.props.entity.changeContent('blah', '12')
^ property `entity`. Property not found in
12: type Props = BaseProps & {
^ object type
Member 2:
12: type Props = BaseProps & { ^ object typeError:
19: this.props.entity.changeContent('blah', '12')
^ string. This type is incompatible with
8: changeContent(content: null | string, selection: null | number): void {
^ union: null | number
Member 1:
8: changeContent(content: null | string, selection: null | number): void {
^ nullError:
19: this.props.entity.changeContent('blah', '12')
^ string. This type is incompatible with
8: changeContent(content: null | string, selection: null | number): void {
^ null
Member 2:
8: changeContent(content: null | string, selection: null | number): void {
^ numberError:
19: this.props.entity.changeContent('blah', '12')
^ string. This type is incompatible with
8: changeContent(content: null | string, selection: null | number): void {
^ number
Which lets us see that the last argument is a string when a number is expected. By changing that, the code now type-checks as expected.
I was kinda right, then :-)
Yeah, absolutely! And this shows something I've noticed lately, that wrong error locations often lead to self-rationalizations that are misleading or wrong! So something I hope Flow continues to improve.
Most helpful comment
Thank you. This is precisely how I ended up fixing the issue, but still:
For sure the documentation needs to be clarified.
Otherwise, no error is reported.
I really wonder when this intersection type is really useful. Looks like it's troublesome to use right !