Flow: Indexable signature not found in ...

Created on 29 Apr 2016  路  7Comments  路  Source: facebook/flow

I keep running into this issue over and over, when adding expando properties onto DOM element or when doing stuff with node.style like following for example:

/* @flow */

const unsetStyleProperty = (node:HTMLElement, propertyName:string) => {
  node.style[propertyName] = ""
}

My guess is that indexable signature is intentionally omitted so that flow can distinguish if properties accessed are known or not. I also found that usually casting objects with non-indexable signature to Object solves the problem:

/* @flow */

const unsetStyleProperty = (node:HTMLElement, propertyName:string) => {
  const style:Object = node.style
  style[propertyName] = ""
}

In the light of it:

  1. I would like to know if casting to Object as illustrated it reasonable solution, or if it's a bug in flow ?
  2. If casting to Object is a way to go, I think it would greatly help users of flow if flow could suggest to use such casting in the "Indexable signature not found in" errors.
  3. If casting to Object as illustrated above is not a proper solution here, maybe different solution can be made available and also included in the "Indexable signature not found in" error message.
object model bug

Most helpful comment

Has there been any movement on this ?

All 7 comments

I think I might be running into a similar issue

Here's my code:

 _.forEach(style, function (prop: string, key: string) {
   $node.style[key] = prop
 })

Here's the error I get:

lib/popups/pop-around.js:378
378:       $node.style[key] = prop
           ^^^^^^^^^^^^^^^^ assignment of computed property/element. Indexable signature not found in
378:       $node.style[key] = prop
           ^^^^^^^^^^^ CSSStyleDeclaration

Casting to an object works for me as well. I'm not sure what the desired solution is?

Also when I use Symbols:

const text = Symbol('text')
class Message {

  [text]: string

  constructor(messageText: string) {
    this[text] = messageText
  }

  text(): string {
    return this[text]
  }
}

lib/test.js:10
 10:   [text]: string
       ^ computed property keys not supported

lib/test.js:13
 13:     this[text] = messageText
         ^^^^^^^^^^ assignment of computed property/element. Indexable signature not found in
  8: class Message {
           ^^^^^^^ Message

lib/test.js:17
 17:     return this[text]
                ^^^^^^^^^^ access of computed property/element. Indexable signature not found in
  8: class Message {
           ^^^^^^^ Message

also like this:

/* @flow */
class Validator {
 foo(str: string): boolean {
    return true;
 }

 bar(str: string): boolean {
   Validator['foo']
 }
}


7:  bar(str: string): boolean {
                      ^ boolean. This type is incompatible with an implicitly-returned undefined.
8:    Validator['foo']      ^ access of computed property/element. Indexable signature not found in
8:    Validator['foo']
      ^ statics of Validator

Also in React component methods that use refs:

  _handleFocusNextField = (ref) => {
    this[ref].TextInput.focus();
  }

"Access of computed property/element: Indexable signature not found in 'Component'.

Or for that matter, component ref definitions themselves:

const REF_INPUT_PASSWORD = 'REF_INPUT_PASSWORD';

...

<TextInput
  ...
  ref={(c) => { this[REF_INPUT_PASSWORD] = c; }}
  ...
/>

Has there been any movement on this ?

Here's my solution for refs (see comments for explanation):

export class SomeComponent extends Component {
  refs: {
      emailInput: number,
      firstNameInput: HTMLInputElement,
      lastNameInput: HTMLInputElement,
      phoneInput: HTMLInputElement,
      referralInput: HTMLInputElement,
      [propName: string]: HTMLInputElement,
  };

  componentDidMount() {
    setTimeout((() => {
      if (this.firstNameInput) {

        // If property is accessed directly using dot notation, 
        // --> it will reference specific prop flow type
        this.refs.firstNameInput.focus();
      }
    }), 1000);
  }

  focusNextField = (nextField: ?string) => {
    if (nextField) {
      const nextFieldRef = `${nextField}Input`;

      // If computed prop is accessed via bracket syntax,
      // --> it will reference index type
      this.refs[nextFieldRef].focus();
      this.setState({activeField: nextField});
    }

     // If you want to use bracket notation, but still get specific flow type,
     // --> you will need to make assertions
    if (nextField) {
      const nextFieldRef = `${nextField}Input`;
      if (nextFieldRef === 'firstNameInput') {

        // The next statement will use the specific,
        // --> 'firstNameInput' type rather than the index type
        this.refs[nextFieldRef].focus();
        this.setState({activeField: nextField});
      }
    }
  };

  // Assign refs using modern approach  (link posted below), 
  // --> but put it within sub-property so that you can use 
  // --> index type in your flow types (above)
  render() {
    return (
      ...
      <input
        type="text"
        ref={(input) => { this.refs.textInput = input; }} 
      />
      ...
    );
  }
}

Assigning refs:
Refs and the DOM

Typing refs:
Adding types for React refs

Let me know if anyone has questions.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ctrlplusb picture ctrlplusb  路  3Comments

Beingbook picture Beingbook  路  3Comments

iamchenxin picture iamchenxin  路  3Comments

mmollaverdi picture mmollaverdi  路  3Comments

jamiebuilds picture jamiebuilds  路  3Comments