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 |
$> 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 |
$> 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 |
$> 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 |
$> 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 |
$> 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 |
$> 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'; |
$> 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 }; |
$> 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; |
$> 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 |
$> 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! } } |
$> 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);
You can edit this page on GitHub and send us a pull request!