Flow 0.99: Callable Properties, Function Statics, and More
Tags: flowtype, flow
Published: 2019-06-01 22:15:18

[Cover image: Text of Time #9](https://unsplash.com/photos/PwQHfxo3Q2Y)

Flow [released 0.99](https://github.com/facebook/flow/releases/tag/v0.99.0) ([comparing changes](https://github.com/facebook/flow/compare/v0.98.0...v0.99.0)) a few days ago. This release contains changes around callable properties, function statics, a few more utilities coming from the React Component realm, bugfixes, and performance improvements for Flow server.

Once again I'm using those bits and bites to understand more about Flow and JavaScript, and this time it'll be mostly around callable properties and function statics, which I'll share about in this post.

<!-- excerpt ends -->

## 🍾 Function statics

Here is one change that caused new errors in our codebase:

**[Make function type statics stricter](https://github.com/facebook/flow/commit/b868f61026b9b34cb22c3f59e1bcbb65a614c71b)**

> Before this change, we optimistically typed the statics of a function type as `any`, under the assumption that a function would not be used as an object. In practice, our optimism here is unfounded.
>
> This diff changes the type of the statics object for function types to an empty, inexact object type with Function.prototype for its **proto**. This choice is sound with respect to the runtime behaviors while also allowing functions with statics to be valid subtypes via width subtyping.

Time for a review on JavaScript. What are _function statics_?

For a JavaScript _class_, a [_static method_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#Static_methods) is a method for the class, not instances of that class.

For a JavaScript _function_, _statics_ refer to those weird moments where you may have a variable that can be accessed by saying function name dot that variable name because functions are also objects in JavaScript 🤷🏻‍♀️. Consider this greeting function with a counter:

```js
function greeting(name) {
  if (!greeting.counter) {
    greeting.counter = 0
  }
  greeting.counter++
  return 'hello, ' + name
}
```

So what this change says is that previously Flow did not put any restrictions on function statics, i.e., they were typed to `any`. In the example above, this would mean that `greeting.counter` is a field of `any`, and is therefore unrestricted.

You may type function statics, for example:

<!-- prettier-ignore-start -->
```js
function greeting(name) {
  (greeting.counter: number)
  if (!greeting.counter) {
    greeting.counter = 0;
  }
  greeting.counter++;
  return 'hello, ' + name;
}
```
<!-- prettier-ignore-end -->

Play around in the [Try Flow](https://flow.org/try/#0PTAEAEDMBsHsHcBQjIFcB2BjALgS1uqAOYBOApmXukQBToCGAtmQJSgDeiooNpFVRAHSZYGbGRIAuUOlSMARhJZdQuSDwCEfSrmrDR6cSTadu3bQP1iJoALygADAG4VAXxUXdQkdZIBqPxducmxUEkIAcgALMmg4ABpQCNA-GSYyF1cgA) and note that if you change the annotation of `counter` to `string`, Flow is catching the error:

```bash
4:   (greeting.counter: string)
      ^ Cannot cast `greeting.counter` to string because number [1] is incompatible with string [2].
References:
6:     greeting.counter = 0;
                          ^ [1]
4:   (greeting.counter: string)
                        ^ [2]
```

### 🐿 New error in our codebase

The real error that happened in our codebase, however, is a bit different.

We write our Redux actions with data wrapped around in a field conventionally referred to as _payload_. And we normally annotate payload like this:

```js
type ActionPayload = {
  data: string,
  error: string,
}
```

Sometimes `payload` can be a function that returns an object containing the data, so we're happy to see this too:

```js
const fetchData = ({
  type,
  payload,
}: {
  type: 'FETCH_DATA',
  payload: () => ActionPayload,
}) => {
  // function body
}
```

In a few occasions, though, we mistyped the payload in the function form, where the payload is actually just the object. So our code worked fine and no one noticed that the annotation isn't making sense at all:

```js
const fetchData = ({
  type,
  payload,
}: {
  type: 'FETCH_DATA',
  payload: () => ActionPayload, // <- wrooooong, or not?
}) => {
  return payload.data
}
```

At 0.99, as function statics are now typed to `{}`, Flow is complaining that we cannot get `data` because `data` is missing in _statics of function type_.

A similar scenario is a mixture of actions and action creators. They get passed around quite often, which makes typing even hairier. I realize that Flow really is getting in our way when we try to be smart in this direction. I like the flexibility but the complaints will keep asking me whether this is a good pattern or not, which I do not yet have an answer.

## 👾 Callable property syntax

**[Remove deprecated `$call` property syntax](https://github.com/facebook/flow/commit/cbd64384dccaddaa8461d7850c24419e2217d13e)**

First of all, to not be confused, this is _not_ [the `$call` utility type](https://flow.org/en/docs/types/utilities/#toc-call), which helps you get the return type of a given function, and is not deprecated nor removed.

The [deprecated `$call` property syntax](https://github.com/facebook/flow/commit/df8ee96bc84b476f874e300b3cd3c07b430e085b) was the precursor of this [callable syntax](https://github.com/facebook/flow/commit/b9db648afe4f2207a49985aa82b90a485a11a900), both the addition of the new syntax and the deprecation of the old happened in [0.75](https://github.com/facebook/flow/releases/tag/v0.75.0).

So what are they about, anyway?

It ties back to the fact we mentioned earlier that with JavaScript functions are also objects. And here's an interesting insight from Flow:

> An object with a callable property can be equivalently viewed as a function with static fields.

And so it turns out:

```js
// You should be able to call objects with call properties
function a(f: { (): string }, g: { (x: number): string }): string {
  return f() + g(123)
}

// ...and get an error if the return type is wrong
function b(f: { (): string }): number {
  return f()
}
```

And similarly:

```js
// You should be able to use an object as a function
function a(x: { (z: number): string }): (z: number) => string {
  return x
}
```

Most amazing #TIL for me is perhaps this:

```js
// Multiple call properties should also be supported
function a(f: { (): string, (x: number): string }): string {
  return f() + f(123)
}

// It should be fine when a function satisfies them all
var b: { (): string, (x: number): string } = function(x?: number): string {
  return 'hi'
}
```

And here is another one that reminds us about functions, that those monadic chained calls made possible by prototypes come down to not much more than a field in a function:

```js
// Expecting properties that do exist should be fine
var b: { apply: Function } = function() {}
```

So that's a really interesting feature that Flow possesses! That means you can annotate memoized functions with Flow and I never even thought of that before learning all this. Consider a _memoized factorial_ ([Try Flow](https://flow.org/try/#0PTAEAEDMBsHsHcBQiAuBPADgU1AWSwLawCWAXlgCYBiAhgMYqwBOxN0AKpjgLygDeiUKDr0AFlgBc-QaACQAbQB2AVwIAjLEwC6Ules0yAvgBoZ8+SOjQtWgBR6NTAJS7Vj04eR1YigM4pQSHpGFjYpfCIySloGZlYOLlBeRSSAPmkhYkhQWwBCINjQ6AA6ETpxJwyhQOC4tlKxHn5PIRbQLJyCkPiG8qwlLVBc7l5lRQosSGJFSkqBatAmLBRlJhSuupKy8QGjGQ2i3p3FQeSkkdAABlAAflAARlBdUAAqGsL4+1AAWgenGSWKzW7269W2-ROiEMQA)) and you can annotate it expressively with Flow:

```js
type MemoizedFactorialType = {
  cache: {
    [number]: number,
  },
  [[call]](number): number,
}

const factorial: MemoizedFactorialType = n => {
  if (!factorial.cache) {
    factorial.cache = {}
  }
  if (factorial.cache[n] !== undefined) {
    return factorial.cache[n]
  }
  factorial.cache[n] = n === 0 ? 1 : n * factorial(n - 1)
  return factorial.cache[n]
}
```

---

There are a few more changes around callable properties in this version, which I'll list out their references:

- [Fix implicit proto for objects with `[[call]]` properties](https://github.com/facebook/flow/commit/5d7e01d06b85388882b3214e383d26b73e6de111)
- [Model multiple call properties as an intersection of callable objects](https://github.com/facebook/flow/commit/b9e26fd3bf7ba10e8f2f8c4e9c90d7f92afe96e1)
- [Concretize callable object params before intersection processing](https://github.com/facebook/flow/commit/0938da8d7293d0077fbe95c3a3e0eebadb57b012)
- [Remove `[[call]]` property from function statics](https://github.com/facebook/flow/commit/3c5a41026f8da473b3869e0ef6d84afa0b978816)

And here is a couple more links to some older commits you may want to look at regarding callable properties:

- [Store object call properties in internal slot](https://github.com/facebook/flow/commit/b9db648afe4f2207a49985aa82b90a485a11a900)
- [Add support for `[[call]]` internal slot properties](https://github.com/facebook/flow/commit/954a72704a6338778c940239573045b28c716488)

## 🦖 Other notable changes

There are few more notable changes I'll just list here for now:

- [Allow function types inside tuples inside arrow return types](https://github.com/facebook/flow/commit/65e6d1df078e778d0244d4ee49bd28c741b5af0f)
- [`React$Config`, `React$ElementRef`, `React$ElementConfig` and `React$ElementProps` are now utility types](https://github.com/facebook/flow/commit/2ac4372e31a1f3a03ae963ed7a89b2118b862aad)
- [`Fragment`'s children are now correctly optional](https://github.com/facebook/flow/commit/6dd62e0d9e0dd3e65a07310abc67a022e6335e1f)
- [Existential type `*` is deprecated](https://github.com/facebook/flow/commit/72462e4f5849f3e5fe27b15bde54aede8b27b8b8)

If you find any of those interesting and have learned something about them, please please write about them because ever since 0.85 my learning about Flow has felt like sitting in a class where no one asks any questions but apparently not many people understand what's going on 🤦🏻‍♀️. Secretly I'm also not completely certain about things I said about Flow. If you spot any mistakes, please do [let me know](https://twitter.com/wgao19/).

## 💯 Till next time

[Flow 0.100](https://github.com/facebook/flow/releases/tag/v0.100.0) is released too. If you haven't noticed, the release notes now come with Try Flow examples. Maybe the added digit indicates a new era.