Making Flow Happy after 0.98
Tags: flow, flowtype
Published: 2019-04-30 13:10:58

It's a Monday after Flow updated 😱  0.98 seems to contain many fixes. Happy to see Flow making more sense version by version. I'm also glad to be learning bits and bites about Flow on the go so I thought I'd share about that :)

Release notes are [here](https://github.com/facebook/flow/releases/tag/v0.98.0), and following are my notes. Flow please be happy 🤞

---

> Turn the deprecated-utility lint on by default

Possible errors: 

- Deprecated utility. Using `$Subtype` types is not recommended! (deprecated-utility)
- Deprecated utility. Using `$Supertype` types is not recommended! (deprecated-utility)



`deprecated-utility` is a lint rule that was added [in 0.89](https://github.com/facebook/flow/releases/tag/v0.89.0). So far `$Subtype` and `$Supertype` are deprecated. However, Flow only starts to complain from this version because `deprecated-utility` is now turned on by default.

You can turn it off by adding back the lint option in `.flowconfig`:

```
# .flowconfig
[lints]
deprecated-utility=off
```

But it's actually not that hard to fix. 

So what were `$Subtype` and `$Supertype` anyway? `$Subtype` normally means what is expected here is more refined than what's inside `$Subtype`. Anyway, that's just my understanding. You can still find the docs [here](https://flow.org/en/docs/lang/subtypes/).

Perhaps you previously used `$Subtype` to refine component props, [as suggested at the time of their deprecation](https://github.com/facebook/flow/commit/d9350718d3e53cdbad1a5739ba95c5f82c0058a7), you can bring back the refinement using bounded generics. 



Some older version of library definitions from Flow-Typed may raise errors. (For example, we were using Redux v3 before Flow 0.89 that caused unhappiness. However, [there is already a libdef update in Flow-Typed](https://github.com/flow-typed/flow-typed/pull/3223).)


I felt a bit hairy about them anyway. Now that they're gone, time to feel happy and get rid of them :)

---

> Changes and fixes around read-only object and array

Possible errors:

- Cannot assign `42` to `x[0]` because read-only arrays cannot be written to.
- Cannot assign `array` to `problem` because read-only array type [1] is incompatible with array type [2].
- Cannot assign `3` to `x.a` because property `a` is not writable.

It's great to see that Flow is constantly improving and how. (Plus with a slight hint of humor to read about the fixes on the previous unsoundedness).

So I've found three changes and fixes around read-only things.

- Read-only arrays cannot be written to with any-typed indexes, [commit](https://github.com/facebook/flow/commit/875cb7a94d22237cb324aa0e896fcf78c99342bf)

This now disallows you to mutate read only arrays. And you may ask, why should I be allowed to mutate them anyway? Turns out previously if the index is of type `any` you would have been able to. 🤷🏻‍♀️

Then there are two more:

- Mixed refines to read-only object under typeof = object, [commit](https://github.com/facebook/flow/commit/0983bd041c6289897c9b33d86e9e14d2b686c2e5)
- Mixed refined by array produces $ReadOnlyArray<mixed>, [commit](https://github.com/facebook/flow/commit/5639532065954e7f66a968af5e68f76a762a9c14)

I do not have a better explanation than the commit messages there. So let me quote:

**[Bugbash] Mixed refines to read-only object under typeof = object**
Summary:
It is not safe to refine `mixed` to `{[string] : mixed}` because this allows writes of any kind into the refined object, and we don't know what type of values the object holds. The only safe operation is to read `mixed` out of the object, so the properties must be read only.

```js
function bad (x : mixed) {
  if (typeof x === "object" && x !== null) {
    x.a = 3;
  }
}

let obj : {a : string} = {a : "oops"};
bad(obj); // yikes
```

And the one with arrays:

**[Bugbash] mixed refined by array produces** `$ReadOnlyArray<mixed>`

Summary:
It is not safe to refine `mixed` to `Array<mixed>` as this allows for writing arbitrary values to the result of the refinement, and since we only know that the value is an array, we cannot know that it is safe to write a given value to the array. We do know, however, that anything we get out of the array will be `mixed`, so it is safe to type it as a `$ReadOnlyArray<mixed>`.

```js
function bad(array: mixed) { 
  if (Array.isArray(array)) { 
    const problem: Array<mixed> = array; problem[1] = 0; 
  }
}
bad((["3"] : Array<string>));
```

---

> Infer void before type checking starts for functions without a return statement

Possible errors:

**Note**: I think the possible errors caused by this fix can be quite variable. Here are two I know of:

- Cannot cast `x.noReturn()` to number because undefined [1] is incompatible with number [2].
- Cannot shadow proto property `yourFunction` [1] because object type [2] is incompatible with undefined [3] in the return value.


Commit on Flow codebase: [Infer void early for functions with no explicit return](https://github.com/facebook/flow/commit/7b1798a5443671010bdf9138985ed25cd6d0a9fe)

If a function does not have a return statement, Flow now infers that to `void`. Interestingly, the most common errors that arise after this were regarding *shadowing proto property*. Check out the following [Try Flow](https://flow.org/try/#0PTAEAEDMBsHsHcBQBLAtgB1gJwC6gFSgCGAzqAEoCmRAxnpFrKqAORbV0sDciiN0pMgFkAngGEmmAHaUpeSgA8csgCZkqtHADoJGWDLkAefAD5QAb0QBIABZEpK6JTHRkNANYAuUAAoAlKAAvGbmAL48oKCIkbb2js6uHv4W0aBWVjT6JLBOWnAA5j4s-G7ulCoAhCx+PFahvFbsDpRYyZbp7DgArlhSoIYqyABuoPoupYHmODbIJFp2Dk7jHqGgwCbW9aFAA):

```jsx
import * as React from 'react';

class MyComponent extends React.Component<*> {
  handleClick: () => {};  // <- {} means an object here

  handleClick() {
    console.log('clicked!');
  }

  render() {
    return <div onClick={this.handleClick} />
  }
}
```

Fixes are simply to annotate them properly `() => void`

---
> Fix a bug which prevented Flow from asking for required type annotations.

This line sounds scary 😱  But I haven't encountered any unhappiness that seems related yet. Will come back here if I do.

---

## References
- [Release notes for 0.98](https://github.com/facebook/flow/releases/tag/v0.98.0)
- [Infer `void` early for functions with no explicit return](https://github.com/facebook/flow/commit/7b1798a5443671010bdf9138985ed25cd6d0a9fe)
- [Try Flow for error caused by shadowing proto property of mismatched annotation](https://flow.org/try/#0PTAEAEDMBsHsHcBQBLAtgB1gJwC6gFSgCGAzqAEoCmRAxnpFrKqAORbV0sDciiN0pMgFkAngGEmmAHaUpeSgA8csgCZkqtHADoJGWDLkAefAD5QAb0QBIABZEpK6JTHRkNANYAuUAAoAlKAAvGbmAL48oKCIkbb2js6uHv4W0aBWVjT6JLBOWnAA5j4s-G7ulCoAhCx+PFahvFbsDpRYyZbp7DgArlhSoIYqyABuoPoupYHmODbIJFp2Dk7jHqGgwCbW9aFAA)
- [Redux 4 Flow-Typed libdef update](https://github.com/flow-typed/flow-typed/pull/3223)