Jake Trent

Flowtype Analyzes All Code Paths

Flowtype gives type errors to help you consider all function code paths.

Other Potential Paths

Often we are only paying attention to the happy path that we’re developing in our code. This may lead to bugs later on that we hadn’t anticipated from the other code paths that exists in different branches of logic.

Flow helps us consider all the code paths and handle each of them in the manner that we’ve indicated is correct in our defined types.

Code With Two Paths

For instance, we might write a function that has some error handling. We declare that the return type of this function is number:

// @flow
const someFunction = (): number => {
  try {
    // ...something that might throw
    return 123
  } catch (err) {
    console.log('handle error...')
  }
}

Do you see the 2nd code path?

One Path Not Handled

So often we consider what’s in the try block and forget to deal with the catch block. That is the case in this example.

When we type check this file by running flow, we get the error:

Error: src/somecode.js:44
 44: const someFunction = (): number => {
                              ^^^^^^ number. This type is incompatible with an implicitly-returned undefined.

It says “this type”, pointing at number, is incompatible with the implicit return that happens in the catch block path of our function. It helps us remember to cover all code paths.

Solution: Change the Code

To make our function do what we said we really wanted – always return a number – we could make the catch path return some error-state value, such as -1:

// @flow
const someFunction = (): number => {
  try {
    return 123
  } catch (err) {
    console.log('handle error...')
    return -1
  }
}

This would be valid for our existing type, and Flow would be happy.

Solution: Change the Type

Another solution would be to use the actual type that we coded for in our original code. The original code returns 123 in the try block but nothing if the catch block is entered.

We could express this with a Maybe type, where the number could be a number or null or undefined. To do this, we put a ? before the number return type.

We would change our type signature to:

const someFunction = (): ?number => {
  try {
    return 123
  } catch (err) {
    console.log('handle error...')
  }
}

This would also make Flow happy by changing the type because that’s actually what the code does.

What other kinds of code path issues has Flow analyzed that have helped you?