Utility Types

$Keys<T>#

In Flow you can use union types similar to enums:

1
2
3
4
type Suit = "Diamonds" | "Clubs" | "Hearts" | "Spades";

const clubs: Suit = 'Clubs';
const wrong: Suit = 'wrong'; // 'wrong' is not a Suit
show Flow output hide Flow output
$> flow
4: const wrong: Suit = 'wrong'; // 'wrong' is not a Suit
                       ^^^^^^^ string. This type is incompatible with
4: const wrong: Suit = 'wrong'; // 'wrong' is not a Suit
                ^^^^ string enum

This is very handy, but sometimes you need to access the enum definition at runtime (i.e. at a value level).

Suppose for example that you want to associate a value to each suit of the previous example.

You could do

1
2
3
4
5
6
7
8
9
10
11
12
13
const suitNumbers = {
  Diamonds: 1,
  Clubs: 2,
  Hearts: 3,
  Spades: 4
};

function printSuitNumber(suit: Suit) {
  console.log(suitNumbers[suit]);
}

printSuitNumber('Diamonds'); // 2
printSuitNumber('foo'); // 'foo' is not a Suit
show Flow output hide Flow output
$> flow
13: printSuitNumber('foo'); // 'foo' is not a Suit
                    ^^^^^ string. This type is incompatible with the expected param type of
8: function printSuitNumber(suit: Suit) {
                                  ^^^^ string enum

but this doesn’t feel very DRY, as we had to explicitly define the suit names twice.

In situations like this one, you can leverage the $Keys<T> operator. Let’s see another example, this time using $Keys:

1
2
3
4
5
6
7
8
9
10
const countries = {
  US: "United States",
  IT: "Italy",
  FR: "France"
};

type Country = $Keys<typeof countries>;

const italy: Country = 'IT';
const nope: Country = 'nope'; // 'nope' is not a Country
show Flow output hide Flow output
$> flow
10: const nope: Country = 'nope'; // 'nope' is not a Country
                          ^^^^^^ property `nope`. Property not found in
10: const nope: Country = 'nope'; // 'nope' is not a Country
                ^^^^^^^ object literal

In the example above, the type of Country is equivalent to type Country = 'US' | 'IT' | 'FR', but Flow was able to extract it from the keys of countries.

$Diff<A, B>#

As the name hints, $Diff<A, B> is the type representing the set difference of A and B, i.e. A \ B, where A and B are both Object Types. Here’s an example:

1
2
3
4
5
6
7
8
9
10
11
type Props = { name: string, age: number };
type DefaultProps = { age: number };
type RequiredProps = $Diff<Props, DefaultProps>;

function setProps(props: RequiredProps) {
  // ...
}

setProps({ name: 'foo' });
setProps({ name: 'foo', age: 42, baz: false }); // you can pass extra props too
setProps({ age: 42 }); // error, name is required
show Flow output hide Flow output
$> flow
11: setProps({ age: 42 }); // error, name is required
    ^^^^^^^^^^^^^^^^^^^^^ function call
5: function setProps(props: RequiredProps) {
                            ^^^^^^^^^^^^^ property `name`. Property not found in
11: setProps({ age: 42 }); // error, name is required
             ^^^^^^^^^^^ object literal

As you may have noticed, the example is not a random one. $Diff is exactly what the React definition file uses to define the type of the props accepted by a React Component.

Class<T>#

Given a type T representing instances of a class C, the type Class<T> is the type of the class C. For example:

1
2
3
4
5
6
7
8
9
10
11
12
class Store {}
class ExtendedStore extends Store {}
class Model {}

function makeStore(storeClass: Class<Store>) {
  return new storeClass();
}

(makeStore(Store): Store);
(makeStore(ExtendedStore): Store);
(makeStore(Model): Model); // error, Class<Model> does not satisfy Class<Store>
(makeStore(ExtendedStore): Model); // Flow infers the return type
show Flow output hide Flow output
$> flow
11: (makeStore(Model): Model); // error, Class<Model> does not satisfy Class<Store>
     ^^^^^^^^^^^^^^^^ Store. This type is incompatible with
11: (makeStore(Model): Model); // error, Class<Model> does not satisfy Class<Store>
                       ^^^^^ Model

11: (makeStore(Model): Model); // error, Class<Model> does not satisfy Class<Store>
               ^^^^^ Model. This type is incompatible with the expected param type of
5: function makeStore(storeClass: Class<Store>) {
                                        ^^^^^ Store

12: (makeStore(ExtendedStore): Model); // Flow infers the return type
     ^^^^^^^^^^^^^^^^^^^^^^^^ Store. This type is incompatible with
12: (makeStore(ExtendedStore): Model); // Flow infers the return type
                               ^^^^^ Model

For classes that take type parameters, you must also provide the parameter. For example:

1
2
3
4
5
6
7
8
9
class ParamStore<T> {
  constructor(data: T) {}
}

function makeParamStore<T>(storeClass: Class<ParamStore<T>>, data: T): ParamStore<T> {
  return new storeClass(data);
}
(makeParamStore(ParamStore, 1): ParamStore<number>);
(makeParamStore(ParamStore, 1): ParamStore<boolean>); // failed because of the second parameter
show Flow output hide Flow output
$> flow
9: (makeParamStore(ParamStore, 1): ParamStore<boolean>); // failed because of the second parameter
                               ^ number. This type is incompatible with
9: (makeParamStore(ParamStore, 1): ParamStore<boolean>); // failed because of the second parameter
                                              ^^^^^^^ boolean

$Supertype<T>#

Work in progress

$Subtype<T>#

Work in progress

$Abstract<T>#

Work in progress

$PropertyType<T, x>#

A $PropertyType is the type at a given key.

As of Flow v0.36.0, x must be a literal string. In future versions, x may be allowed to be any type, as long as that type exists on the keys of T.

1
2
3
4
5
6
7
8
9
type Person = {
  name: string,
  age: number,
  parent: Person
};

const newName: $PropertyType<Person, 'name'> = 'Michael Jackson';
const newAge: $PropertyType<Person, 'age'> = 50;
const newParent: $PropertyType<Person, 'parent'> = 'Joe Jackson';
show Flow output hide Flow output
$> flow
9: const newParent: $PropertyType<Person, 'parent'> = 'Joe Jackson';
                                                      ^^^^^^^^^^^^^ string. This type is incompatible with
9: const newParent: $PropertyType<Person, 'parent'> = 'Joe Jackson';
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ object type

This can be especially useful for referring to the type of React props, or, even the entire props type itself.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React from 'react';
class Tooltip extends React.Component {
  props: {
    text: string,
    onMouseOver: ({x: number, y: number}) => void
  };
}

const someProps: $PropertyType<Tooltip, 'props'> = {
  text: 'foo',
  onMouseOver: (data: {x: number, y: number}) => undefined
};

const otherProps: $PropertyType<Tooltip, 'props'> = {
  text: 'foo'
  // Missing the `onMouseOver` definition
};
show Flow output hide Flow output
$> flow
5:     onMouseOver: ({x: number, y: number}) => void
                     ^^^^^^^^^^^^^^^^^^^^^^ object type. This type is incompatible with the expected param type of
24: const handler2: PositionHandler = (data: string) => undefined; // wrong parameter types
                                             ^^^^^^ string

14: const otherProps: $PropertyType<Tooltip, 'props'> = {
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ property `onMouseOver`. Property not found in
14: const otherProps: $PropertyType<Tooltip, 'props'> = {
                                                        ^ object literal

You can even nest lookups:

type PositionHandler = $PropertyType<$PropertyType<Tooltip, 'props'>, 'onMouseOver'>;
const handler: PositionHandler = (data: {x: number, y: number}) => undefined;
const handler2: PositionHandler = (data: string) => undefined; // wrong parameter types

You can use this in combination with Class<T> to get static props:

1
2
3
4
5
6
7
class BackboneModel {
  static idAttribute: string | false;
}

type ID = $PropertyType<Class<BackboneModel>, 'idAttribute'>;
const someID: ID = '1234';
const someBadID: ID = true;
show Flow output hide Flow output
$> flow
7: const someBadID: ID = true;
                         ^^^^ boolean. This type is incompatible with
7: const someBadID: ID = true;
                    ^^ union: string | boolean literal `false`

*#

* is known as the existential type.

An existential type is used as a placeholder to tell Flow to infer the type.

For example, in the Class<ParamStore<T>> example, we could have used an existential type for the return:

1
2
3
4
5
function makeParamStore<T>(storeClass: Class<ParamStore<T>>, data: T): * {
  return new storeClass(data);
}
(makeParamStore(ParamStore, 1): ParamStore<number>);
(makeParamStore(ParamStore, 1): ParamStore<boolean>); // failed because of the second parameter
show Flow output hide Flow output
$> flow
5: (makeParamStore(ParamStore, 1): ParamStore<boolean>); // failed because of the second parameter
                               ^ number. This type is incompatible with
5: (makeParamStore(ParamStore, 1): ParamStore<boolean>); // failed because of the second parameter
                                              ^^^^^^^ boolean

The * can be thought of as an “auto” instruction to Flow, telling it to fill in the type from context.

In comparison to any, * may allow you to avoid losing type safety.

The existential operator is also useful for automatically filling in types without unnecessary verbosity:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class DataStore {
  data: *; // If this property weren't defined, you'd get an error just trying to assign `data`
  constructor() {
    this.data = {
      name: 'DataStore',
      isOffline: true
    };
  }
  goOnline() {
    this.data.isOffline = false;
  }
  changeName() {
    this.data.isOffline = 'SomeStore'; // oops, wrong key!
  }
}
show Flow output hide Flow output
$> flow
13:     this.data.isOffline = 'SomeStore'; // oops, wrong key!
                              ^^^^^^^^^^^ string. This type is incompatible with
6:       isOffline: true
                    ^^^^ boolean

$Exact<T>#

$Exact<{name: string}> is a synonym for {| name: string |} as in the Object documentation.

type ExactUser = $Exact<{name: string}>;
type ExactUserShorthand = {| name: string |};

const user2 = {name: 'John Wilkes Booth'};
// These will both be satisified because they are equivalent
(user2: ExactUser);
(user2: ExactUserShorthand);

← Prev Next →

You can edit this page on GitHub and send us a pull request!