Utility Types

Creating complex types with utility types

Flow provides a set of utility types to operate on other types, and can be useful for different scenarios.

Table of contents:

$Keys<T>

In Flow you can use union types similar to enums:

1
2
3
4
5
// @flow
type Suit = "Diamonds" | "Clubs" | "Hearts" | "Spades";

const clubs: Suit = 'Clubs';
const wrong: Suit = 'wrong'; // 'wrong' is not a Suit
Cannot assign `'wrong'` to `wrong` because string [1] is incompatible with enum [2].

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
14
15
16
// @flow
type Suit = "Diamonds" | "Clubs" | "Hearts" | "Spades";

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
Cannot call `printSuitNumber` with `'foo'` bound to `suit` because string [1] is incompatible with enum [2].

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
11
// @flow
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
Cannot assign `'nope'` to `nope` because property `nope` is missing in `typeof countries` [1].

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.

$Values<T>

$Values<T> represents the union type of all the value types of the enumerable properties in an Object Type T.

For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
// @flow
type Props = {
  name: string,
  age: number,
};

// The following two types are equivalent:
type PropValues = string | number;
type Prop$Values = $Values<Props>;

const name: Prop$Values = 'Jon';  // OK
const age: Prop$Values = 42;  // OK
const fn: Prop$Values = () => {};  // Error! function is not part of the union type
Cannot assign function to `fn` because: Either function [1] is incompatible with string [2]. Or function [1] is incompatible with number [3].

$ReadOnly<T>

$ReadOnly<T> is a type that represents the read-only version of a given object type T. A read-only object type is an object type whose keys are all read-only.

This means that the following 2 types are equivalent:

1
2
3
type ReadOnlyObj = {
  +key: any,  // read-only field, marked by the `+` annotation
};
1
2
3
type ReadOnlyObj = $ReadOnly<{
  key: any,
}>;

This is useful when you need to use a read-only version of an object type you’ve already defined, without manually having to re-define and annotate each key as read-only. For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// @flow
type Props = {
  name: string,
  age: number,
  // ...
};

type ReadOnlyProps = $ReadOnly<Props>;

function render(props: ReadOnlyProps) {
  const {name, age} = props;  // OK to read
  props.age = 42;             // Error when writing
  // ...
}
Cannot assign `42` to `props.age` because property `age` is not writable.

Additionally, other utility types, such as $ObjMap<T>, may strip any read/write annotations, so $ReadOnly<T> is a handy way to quickly make the object read-only again after operating on it:

1
2
3
4
5
type Obj = {
  +key: any,
};

type MappedObj = $ReadOnly<$ObjMap<Obj, TypeFn>> // Still read-only

$Exact<T>

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

1
2
3
4
5
6
7
8
// @flow
type ExactUser = $Exact<{name: string}>;
type ExactUserShorthand = {| name: string |};

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

$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
12
// @flow
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
Cannot call `setProps` with object literal bound to `props` because property `name` is missing in object literal [1] but exists in `Props` [2].

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.

Note that $Diff<A, B> will error if the object you are removing properties from does not have the property being removed, i.e. if B has a key that doesn’t exist in A:

1
2
3
4
5
6
7
8
9
// @flow
type Props = { name: string, age: number };
type DefaultProps = { age: number, other: string }; // Will error due to this `other` property not being in Props.
type RequiredProps = $Diff<Props, DefaultProps>;

function setProps(props: RequiredProps) {
  props.name;
  // ...
}
Cannot instantiate `$Diff` because undefined property `other` [1] is incompatible with string [2].

As a workaround, you can specify the property not present in A as optional. For example:

1
2
type A = $Diff<{}, {nope: number}>; // Error
type B = $Diff<{}, {nope: number | void}>; // OK

$Rest<A, B>

$Rest<A, B> is the type that represents the runtime object rest operation, e.g.: const {foo, ...rest} = obj, where A and B are both object types. The resulting type from this operation will be an object type containing A’s own properties that are not own properties in B. In flow, we treat all properties on exact object types as own. In in-exact objects, a property may or may not be own.

For example:

1
2
3
4
5
6
7
// @flow
type Props = { name: string, age: number };

const props: Props = {name: 'Jon', age: 42};
const {age, ...otherProps} = props;
(otherProps: $Rest<Props, {|age: number|}>);
otherProps.age;  // Error
Cannot cast `otherProps` to `$Rest` because string [1] is incompatible with undefined [1] in property `name`. Cannot get `otherProps.age` because property `age` is missing in `otherProps` [1].

The main difference with $Diff<A, B>, is that $Rest<A, B> aims to represent the true runtime rest operation, which implies that exact object types are treated differently in $Rest<A, B>. For example, $Rest<{|n: number|}, {}> will result in {|n?: number|} because an in-exact empty object may have an n property, while $Diff<{|n: number|}, {}> will result in {|n: number|}.

$PropertyType<T, k>

A $PropertyType<T, k> is the type at a given key k. As of Flow v0.36.0, k must be a literal string.

1
2
3
4
5
6
7
8
9
10
// @flow
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';
Cannot assign `'Joe Jackson'` to `newParent` because string [1] is incompatible with `Person` [2].

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
18
// @flow
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
};
Cannot use property `Component` [1] with less than 1 type argument. Cannot assign object literal to `otherProps` because property `onMouseOver` is missing in object literal [1] but exists in object type [2].

You can even nest lookups:

1
2
3
4
// @flow
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
Cannot resolve name `Tooltip`.

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

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

type ID = $PropertyType<Class<BackboneModel>, 'idAttribute'>;
const someID: ID = '1234';
const someBadID: ID = true;
Cannot assign `true` to `someBadID` because: Either boolean [1] is incompatible with string [2]. Or boolean [1] is incompatible with boolean literal `false` [3].

$ElementType<T, K>

$ElementType<T, K> is the type that represents the type of every element inside an array, tuple or object type T, that matches the given key type K.

For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// @flow

// Using objects:
type Obj = {
  name: string,
  age: number,
}
('Jon': $ElementType<Obj, 'name'>);
(42: $ElementType<Obj, 'age'>);
(true: $ElementType<Obj, 'name'>); // Nope, `name` is not a boolean
(true: $ElementType<Obj, 'other'>); // Nope, property `other` is not in Obj

// Using tuples:
type Tuple = [boolean, string];
(true: $ElementType<Tuple, 0>);
('foo': $ElementType<Tuple, 1>);
('bar': $ElementType<Tuple, 2>); // Nope, can't access position 2
Cannot cast `true` to `$ElementType` because boolean [1] is incompatible with string [2]. Cannot instantiate `$ElementType` because property `other` is missing in `Obj` [1]. Cannot cast `'bar'` to `$ElementType` because string [1] is incompatible with undefined (out of bounds tuple access) [2]. Cannot instantiate `$ElementType` because `Tuple` [1] only has 2 elements, so index 2 is out of bounds.

In the above case, we’re using literal values as K, similarly to $PropertyType<T, k>. However, when using $ElementType<T, K>, K is allowed to be any type, as long as that type exists on the keys of T. For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// @flow

// Using objects
type Obj = { [key: string]: number };
(42: $ElementType<Obj, string>);
(42: $ElementType<Obj, boolean>); // Nope, object keys aren't booleans
(true: $ElementType<Obj, string>); // Nope, elements are numbers


// Using arrays, we don't statically know the size of the array, so you can just use the `number` type as the key:
type Arr = Array<boolean>;
(true: $ElementType<Arr, number>);
(true: $ElementType<Arr, boolean>); // Nope, array indices aren't booleans
('foo': $ElementType<Arr, number>); // Nope, elements are booleans
boolean [1] is incompatible with string [2]. Cannot cast `true` to `$ElementType` because boolean [1] is incompatible with number [2]. Cannot instantiate `$ElementType` because boolean [1] is not an array index. Cannot cast `'foo'` to `$ElementType` because string [1] is incompatible with boolean [2].

You can also nest calls to $ElementType<T, K>, which is useful when you need to access the types inside nested structures:

1
2
3
4
5
6
// @flow
type NumberObj = {
  nums: Array<number>,
};

(42: $ElementType<$ElementType<NumberObj, 'nums'>, number>);

Additionally, one of the things that also makes $ElementType<T, K> more powerful than $PropertyType<T, k> is that you can use it with generics. For example:

1
2
3
4
5
6
7
8
// @flow
function getProp<O: {+[string]: mixed}, P: $Keys<O>>(o: O, p: P): $ElementType<O, P> {
  return o[p];
}

(getProp({a: 42}, 'a'): number); // OK
(getProp({a: 42}, 'a'): string); // Error: number is not a string
getProp({a: 42}, 'b'); // Error: `b` does not exist
Cannot cast `getProp(...)` to string because number [1] is incompatible with string [2]. Cannot call `getProp` with object literal bound to `o` because property `b` is missing in object literal [1]. Cannot call `getProp` with `'b'` bound to `p` because property `b` is missing in object literal [1].

$NonMaybeType<T>

$NonMaybeType<T> converts a type T to a non-maybe type. In other words, the values of $NonMaybeType<T> are the values of T except for null and undefined.

1
2
3
4
5
6
7
8
// @flow
type MaybeName = ?string;
type Name = $NonMaybeType<MaybeName>;

('Gabriel': MaybeName); // Ok
(null: MaybeName); // Ok
('Gabriel': Name); // Ok
(null: Name); // Error! null can't be annotated as Name because Name is not a maybe type
Cannot cast `null` to `Name` because: Either null [1] is incompatible with empty [2]. Or null [1] is incompatible with empty [2]. Or null [1] is incompatible with string [2].

$ObjMap<T, F>

ObjMap<T, F> takes an object type T, and a function type F, and returns the object type obtained by mapping the type of each value in the object with the provided function type F. In other words, $ObjMap will call (at the type level) the given function type F for every property value type in T, and return the resulting object type from those calls.

Let’s see an example. Suppose you have a function called run that takes an object of thunks (functions in the form () => A) as input:

1
2
3
4
// @flow
function run<O: {[key: string]: Function}>(o: O) {
  return Object.keys(o).reduce((acc, k) => Object.assign(acc, { [k]: o[k]() }), {});
}

The function’s purpose is to run all the thunks and return an object made of values. What’s the return type of this function?

The keys are the same, but the values have a different type, namely the return type of each function. At a value level (the implementation of the function) we’re essentially mapping over the object to produce new values for the keys. How to express this at a type level?

This is where ObjMap<T, F> comes in handy.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// @flow

// let's write a function type that takes a `() => V` and returns a `V` (its return type)
type ExtractReturnType = <V>(() => V) => V

function run<O: {[key: string]: Function}>(o: O): $ObjMap<O, ExtractReturnType> {
  return Object.keys(o).reduce((acc, k) => Object.assign(acc, { [k]: o[k]() }), {});
}

const o = {
  a: () => true,
  b: () => 'foo'
};

(run(o).a: boolean); // Ok
(run(o).b: string);  // Ok
// $ExpectError
(run(o).b: boolean); // Nope, b is a string
// $ExpectError
run(o).c;            // Nope, c was not in the original object
Cannot cast `run(...).b` to boolean because string [1] is incompatible with boolean [2]. Cannot get `run(...).c` because property `c` is missing in object type [1].

This is extremely useful for expressing the return type of functions that manipulate objects values. You could use a similar approach (for instance) to provide the return type of bluebird’s Promise.props function, which is like Promise.all but takes an object as input.

Here’s a possible declaration of this function, which is very similar to our first example:

1
2
// @flow
declare function props<A, O: { [key: string]: A }>(promises: O): Promise<$ObjMap<O, typeof $await>>;

And use:

1
2
3
4
5
6
7
// @flow
const promises = { a: Promise.resolve(42) };
props(promises).then(o => {
  (o.a: 42); // Ok
  // $ExpectError
  (o.a: 43); // Error, flow knows it's 42
});
Cannot resolve name `props`.

$TupleMap<T, F>

$TupleMap<T, F> takes an iterable type T (e.g.: Tuple or Array), and a function type F, and returns the iterable type obtained by mapping the type of each value in the iterable with the provided function type F. This is analogous to the Javascript function map.

Following our example from $ObjMap<T>, let’s assume that run takes an array of functions, instead of an object, and maps over them returning an array of the function call results. We could annotate its return type like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
// @flow

// Function type that takes a `() => V` and returns a `V` (its return type)
type ExtractReturnType = <V>(() => V) => V

function run<A, I: Array<() => A>>(iter: I): $TupleMap<I, ExtractReturnType> {
  return iter.map(fn => fn());
}

const arr = [() => 'foo', () => 'bar'];
(run(arr)[0]: string); // OK
(run(arr)[1]: string); // OK
(run(arr)[1]: boolean); // Error
Cannot cast `run(...)[1]` to boolean because string [1] is incompatible with boolean [2].

$Call<F>

$Call<F> is a type that that represents the result of calling the given function type F. This is analogous to calling a function at runtime (or more specifically, it’s analogous to calling Function.prototype.call), but at the type level; this means that function type calls happens statically, i.e. not at runtime.

Let’s see a couple of examples:

1
2
3
4
5
6
7
8
9
10
11
// @flow

// Takes an object type, returns the type of its `prop` key
type ExtractPropType = <T>({prop: T}) => T;
type Obj = {prop: number};
type PropType = $Call<ExtractPropType, Obj>;  // Call `ExtractPropType` with `Obj` as an argument
type Nope = $Call<ExtractPropType, {nope: number}>;  // Error: argument doesn't match `Obj`.

(5: PropType); // OK
(true: PropType);  // Error: PropType is a number
(5: Nope);  // Error
Cannot instantiate `$Call` because property `prop` is missing in object type [1] but exists in object type [2] in the first argument. Cannot cast `true` to `PropType` because boolean [1] is incompatible with number [2].
1
2
3
4
5
6
7
8
9
10
// @flow

// Takes a function type, and returns its return type
// This is useful if you want to get the return type of some function without actually calling it at runtime.
type ExtractReturnType = <R>(() => R) => R;
type Fn = () => number;
type ReturnType = $Call<ExtractReturnType, Fn> // Call `ExtractReturnType` with `Fn` as an argument

(5: ReturnType);  // OK
(true: ReturnType);  // Error: ReturnType is a number
Cannot cast `true` to `ReturnType` because boolean [1] is incompatible with number [2].

$Call can be very powerful because it allows you to make calls in type-land that you would otherwise have to do at runtime. The type-land calls happen statically and will be erased at runtime.

Let’s look at a couple of more advanced examples:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// @flow

// Extracting deeply nested types:
type NestedObj = {|
  +status: ?number,
  +data: ?$ReadOnlyArray<{|
    +foo: ?{|
       +bar: number,
    |},
  |}>,
|};

// If you wanted to extract the type for `bar`, you could use $Call:
type BarType = $Call<
  <T>({
    +data: ?$ReadOnlyArray<{
      +foo: ?{
        +bar: ?T
      },
    }>,
  }) => T,
  NestedObj,
>;

(5: BarType);
(true: BarType);  // Error: `bar` is not a boolean
Cannot cast `true` to `BarType` because boolean [1] is incompatible with number [2].
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// @flow

// Getting return types:
function getFirstValue<V>(map: Map<string, V>): ?V {
  for (const [key, value] of map.entries()) {
    return value;
  }
  return null;
}

// Using $Call, we can get the actual return type of the function above, without calling it at runtime:
type Value = $Call<typeof getFirstValue, Map<string, number>>;

(5: Value);
(true: Value);  // Error: Value is a `number`


// We could generalize it further:
type GetMapValue<M> =
  $Call<typeof getFirstValue, M>;

(5: GetMapValue<Map<string, number>>);
(true: GetMapValue<Map<string, boolean>>);
(true: GetMapValue<Map<string, number>>);  // Error: value is a `number`
Cannot cast `true` to `Value` because boolean [1] is incompatible with number [2]. Cannot cast `true` to `GetMapValue` because boolean [1] is incompatible with number [2].

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
13
// @flow
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
(makeStore(ExtendedStore): Model); // Flow infers the return type
Cannot cast `makeStore(...)` to `Model` because `Store` [1] is incompatible with `Model` [2]. Cannot call `makeStore` with `Model` bound to `storeClass` because `Model` [1] is incompatible with `Store` [2]. Cannot cast `makeStore(...)` to `Model` because `Store` [1] is incompatible with `Model` [2].

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

1
2
3
4
5
6
7
8
9
10
// @flow
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
Cannot cast `makeParamStore(...)` to `ParamStore` because number [1] is incompatible with boolean [2] in type argument `T` [3].

$Supertype<T>

Work in progress

$Subtype<T>

Work in progress

Existential Type (*)

* 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
6
// @flow
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
Cannot resolve name `ParamStore`.

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
16
// @flow
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!
  }
}
Cannot assign `'SomeStore'` to `this.data.isOffline` because string [1] is incompatible with boolean [2].

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