Flow: bind this to method in constructor issue

Created on 12 Feb 2016  ยท  9Comments  ยท  Source: facebook/flow

class CameraRollView extends Component{

    constructor(props){
        super(props);
        this.state =  getInitState(this);
        this.rendererChanged = this.rendererChanged.bind(this);
        this._fetch = this._fetch.bind(this);
        this.fetch = this.fetch.bind(this);
        this._rowHasChanged = this._rowHasChanged.bind(this);
        this._renderFooterSpinner = this._renderFooterSpinner.bind(this);
        this._renderRow = this._renderRow.bind(this);
        this._appendAssets = this._appendAssets.bind(this);
        this._onEndReached = this._onEndReached.bind(this);
    }
components/CameraRollView.js:80
 80:        this._fetch = this._fetch.bind(this);
            ^^^^^^^^^^^ assignment of property `_fetch`
 80:        this._fetch = this._fetch.bind(this);
                 ^^^^^^ property `_fetch`. Property not found in
 74: class CameraRollView extends Component{
           ^^^^^^^^^^^^^^ CameraRollView

components/CameraRollView.js:82
 82:        this._rowHasChanged = this._rowHasChanged.bind(this);
            ^^^^^^^^^^^^^^^^^^^ assignment of property `_rowHasChanged`
 82:        this._rowHasChanged = this._rowHasChanged.bind(this);
                 ^^^^^^^^^^^^^^ property `_rowHasChanged`. Property not found in
 74: class CameraRollView extends Component{
           ^^^^^^^^^^^^^^ CameraRollView

components/CameraRollView.js:83
 83:        this._renderFooterSpinner = this._renderFooterSpinner.bind(this);
            ^^^^^^^^^^^^^^^^^^^^^^^^^ assignment of property `_renderFooterSpinner`
 83:        this._renderFooterSpinner = this._renderFooterSpinner.bind(this);
                 ^^^^^^^^^^^^^^^^^^^^ property `_renderFooterSpinner`. Property not found in
 74: class CameraRollView extends Component{
           ^^^^^^^^^^^^^^ CameraRollView

components/CameraRollView.js:84
 84:        this._renderRow = this._renderRow.bind(this);
            ^^^^^^^^^^^^^^^ assignment of property `_renderRow`
 84:        this._renderRow = this._renderRow.bind(this);
                 ^^^^^^^^^^ property `_renderRow`. Property not found in
 74: class CameraRollView extends Component{
           ^^^^^^^^^^^^^^ CameraRollView

components/CameraRollView.js:85
 85:        this._appendAssets = this._appendAssets.bind(this);
            ^^^^^^^^^^^^^^^^^^ assignment of property `_appendAssets`
 85:        this._appendAssets = this._appendAssets.bind(this);
                 ^^^^^^^^^^^^^ property `_appendAssets`. Property not found in
 74: class CameraRollView extends Component{
           ^^^^^^^^^^^^^^ CameraRollView

Most helpful comment

Another workaround we've seen is to write (this:any).foo = this.foo.bind(this) where the (this:any) tells Flow to shut up about this error.

The reason why Flow rejects this code is because we take class methods to be readonly. At runtime, they are read/write, but the readonly assumption is extremely convenient in that it allows subclasses to override methods covariantly, which is an even more common pattern than rebinding.

That said, improvements in this space are likely in the future. We should also make sure we document this when we update the docs.

All 9 comments

TLDR: This is a known issue. The workaround is to write property annotations for the re-bound methods.

Flow expects you to explicitly specify all expected instance properties on a class. Assigning to this means creating an instance property -- and when you bind methods in this way, you are copying them to the instance as a property.

So the workaround is to explicitly specify these re-bound methods as properties on the class as such:

class Foo {
  _fetch;
  _rowHasChanged;
  ...

  constructor(props){
        super(props);
        this.state =  getInitState(this);
        this._fetch = this._fetch.bind(this);
        this._rowHasChanged = this._rowHasChanged.bind(this);
        ...
    }
}

Another workaround we've seen is to write (this:any).foo = this.foo.bind(this) where the (this:any) tells Flow to shut up about this error.

The reason why Flow rejects this code is because we take class methods to be readonly. At runtime, they are read/write, but the readonly assumption is extremely convenient in that it allows subclasses to override methods covariantly, which is an even more common pattern than rebinding.

That said, improvements in this space are likely in the future. We should also make sure we document this when we update the docs.

Why do you close this issue? Is this resolve?

Still no fixes yet?

Wow, after searching for this problem I found this issue (again).

Any new recommendations?

For now I've started using autobind-decorator.

Class methods are expected to be read-only. Rather than a "known issue" I'd argue this is expected behaviour, and to "override" the read-only assumption of class methods you should use @jeffmo's approach

With flow strict folks can't use the (this: any) workaround, instead having to do the duplicative work of declaring the function as an instance variable.

Error โ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆ packages/mathjax/src/context.js:57:12

Unclear type. Using any, Object, Function, $Subtype<...>, or $Supertype<...> types is not safe! (unclear-type)

     54โ”‚   constructor(props: Props) {
     55โ”‚     super(props);
     56โ”‚     this.state = { loaded: false };
     57โ”‚     (this: any).onLoad = this.onLoad.bind(this);
     58โ”‚   }
     59โ”‚
     60โ”‚   getChildContext() {

What's a bit bothersome is that I'd prefer to be able to let Flow infer the function definition elsewhere as this is meant to be a read-only property. This is totally to make the true this available for React rendering anyways. ๐Ÿค—

The way objects work is being re-worked a bit. I don't know the full details but I hear a the way classes/this works is changing a bit, keep an eye out in the next month or two ๐Ÿ‘

Still now fixes, why this issue is closed ?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vjpr picture vjpr  ยท  55Comments

blax picture blax  ยท  83Comments

StoneCypher picture StoneCypher  ยท  253Comments

jamesisaac picture jamesisaac  ยท  44Comments

Macil picture Macil  ยท  186Comments