FAQ

Frequently asked questions about using Flow

I checked that foo.bar is not null, but Flow still thinks it is. Why does this happen and how can I fix it?

Flow does not keep track of side effects, so any function call may potentially nullify your check. This is called refinement invalidation.

Example (https://flow.org/try):

1
2
3
4
5
6
7
8
9
10
11
12
// @flow
type Param = {
  bar: ?string,
}
function myFunc(foo: Param): string {
  if (foo.bar) {
    console.log("checked!");
    return foo.bar; // Flow errors. If you remove the console.log, it works
  }

  return "default string";
}
Cannot return `foo.bar` because null or undefined [1] is incompatible with string [2].

You can get around this by storing your checked values in local variables:

1
2
3
4
5
6
7
8
9
10
11
12
13
// @flow
type Param = {
  bar: ?string,
}
function myFunc(foo: Param): string {
  if (foo.bar) {
    const bar = foo.bar;
    console.log("checked!");
    return bar; // Ok!
  }

  return "default string";
}

I checked that my object is of type A, so why does Flow still believe it’s A | B?

Refinement invalidation can also happen with disjoint unions. Any function call will invalidate any refinement.

Example (https://flow.org/try):

1
2
3
4
5
6
7
8
9
10
11
12
// @flow
type Response =
  | { type: 'success', value: string }
  | { type: 'error', error: Error };

const handleResponse = (response: Response) => {
  if (response.type === 'success') {
    setTimeout(() => {
      console.log(`${response.value} 1`)
    }, 1000);
  }
};
Cannot get `response.value` because property `value` is missing in object type [1].

Here, a work around would be to extract the part of the value you’re interested in, or to move the if check inside the setTimeout call:

Example (https://flow.org/try):

1
2
3
4
5
6
7
8
9
10
11
12
13
// @flow
type Response =
  | { type: 'success', value: string }
  | { type: 'error', error: Error };

const handleResponse = (response: Response) => {
  if (response.type === 'success') {
    const value = response.value
    setTimeout(() => {
      console.log(`${value} 1`)
    }, 1000);
  }
};

I’m in a closure and Flow ignores the if check that asserts that foo.bar is defined. Why?

In the previous section we showed how refinement is lost after a function call. The exact same thing happens within closures, since Flow does not track how your value might change before the closure is called.

Example (https://flow.org/try):

1
2
3
4
5
6
7
8
9
// @flow
type Person = {age: ?number}
const people = [{age: 12}, {age: 18}, {age: 24}];
const oldPerson: Person = {age: 70};
if (oldPerson.age) {
  people.forEach(person => {
    console.log(`The person is ${person.age} and the old one is ${oldPerson.age}`);
  })
}
Cannot coerce `oldPerson.age` to string because null or undefined [1] should not be coerced.

The solution here is to move the if check in the forEach, or to assign the age to an intermediate variable.

Example (https://flow.org/try):

1
2
3
4
5
6
7
8
9
10
// @flow
type Person = {age: ?number}
const people = [{age: 12}, {age: 18}, {age: 24}];
const oldPerson: Person = {age: 70};
if (oldPerson.age) {
  const age = oldPerson.age;
  people.forEach(person => {
    console.log(`The person is ${person.age} and the old one is ${age}`);
  })
}

But Flow should understand that this function cannot invalidate this refinement, right?

Flow is not complete, so it cannot check all code perfectly. Instead, Flow will make conservative assumptions to try to be sound.

Why can’t I use a function in my if-clause to check the type of a property?

Flow doesn’t track refinements made in separated function calls.

Example (https://flow.org/try)

1
2
3
4
5
// @flow
const add = (first: number, second: number) => first + second;
const val: string | number = ...
const isNumber = (valueToRefine: ?number) => typeof valueToRefine === 'number';
if (isNumber(val)) add(val, 2);
Unexpected token ...

However, Flow has predicates functions that can do these checks via %checks.

Example (https://flow.org/try)

1
2
3
4
5
// @flow
const add = (first: number, second: number) => first + second;
const val: string | number = ...
const isNumber = (valueToRefine: ?number): %checks => typeof valueToRefine === 'number';
if (isNumber(val)) add(val, 2);
Unexpected token ...

I got a “Missing type annotation” error. Where does it come from?

Flow requires type annotations at module boundaries to make sure it can scale. To read more about that, check out our blog post about that.

The most common case you’ll encounter is when exporting a function or React component. Flow requires you to annotate inputs. For instance, in this example, flow will complain:

1
2
// @flow
export const add = a => a + 1;
Missing type annotation for `a`.

The fix here is to add types to the parameters of add.

Example (https://flow.org/try):

1
2
// @flow
export const add = (a: number) => a + 1;

To see how you can annotate exported React components, check out our docs on HOCs.

There are other cases where this happens, and they might be harder to understand. You’ll get an error like Missing type annotation for U For instance, you wrote this code:

1
2
3
// @flow
const array = ['a', 'b']
export const genericArray = array.map(a => a)
Missing type annotation for `U`. `U` is a type parameter declared in function type [1] and was implicitly instantiated at call of method `map` [2].

Here, Flow will complain on the export, asking for a type annotation. Flow wants you to annotate exports returned by a generic function. The type of Array.prototype.map is map<U>(callbackfn: (value: T, index: number, array: Array<T>) => U, thisArg?: any): Array<U>. The <U> corresponds to what is called a generic, to express the fact that the type of the function passed to map is linked to the type of the array.

Understanding the logic behind generics might be useful, but what you really need to know to make your typings valid is that you need to help Flow to understand the type of genericArray.

You can do that by adding an explicit type argument:

1
2
3
// @flow
const array = ['a', 'b'];
export const genericArray = array.map<string>(a => a);

or by annotating the exported constant (https://flow.org/try):

1
2
3
// @flow
const array = ['a', 'b']
export const genericArray: Array<string> = array.map(a => a)

Flow cannot understand the types of my higher order React component, how can I help it?

Typings HOCs can be complicated. While you can follow the docs about it, sometimes it can be easier to type the returned component.

For instance, in this example, we don’t type the HOC (setType), but the component created with it, Button. To do so, we use the type React.ComponentType.

1
2
3
4
5
6
7
// @flow
import * as React from 'react';

const setType = BaseComponent => props => <BaseComponent {...props} type="button" />;
const GenericButton = ({type, children}) => <button type={type} onClick={() => console.log('clicked')}>{children}</button>;

const Button: React.ComponentType<{children: React.Node}> = setType(GenericButton);


Was this guide helpful? Let us know by sending a message to @flowtype.