Flow is a type checker made by Facebook for checking JavaScript data types. It has many built-in data types we can use to annotate the types of variables and function parameters.
In this article, we’ll look at the built-in some utility types that come with Flow.
$Keys<T>
The $Keys<T>
type gets the properties from a type and uses it as its own type. It’s also useful for creating enums.
For example, we can write:
const fruits = {
apple: 'Apple',
orange: 'Orange',
banana: 'Banana'
};
type Fruit = $Keys<typeof fruits>;
let a: Fruit = 'apple';
The code above will define a new type Fruit
with the union of the keys of the fruits
object. This means that with a variable of type Fruit
, we can assign 'apple'
, 'orange'
or 'banana'
to it.
Assigning anything else should get us an error:
let b: Fruit = 'grape';
$Values<T>
The $Values<T>
type let us get the types of an object type and create a type from a union of them.
For example, we can write:
type Person = {
name: string,
age: number,
};
type PersonValues = $Values<Person>;
let a: PersonValues = 1;
let b: PersonValues = 'Joe';
In the code, we defined the Person
type, with some properties, and then $Values<Person>
takes the types of name
and age
and create a union from it. So the type of PeronValues
is string | number
.
$ReadOnly<T>
$ReadOnly<T>
gets us a read-only version of type T
.
The following:
type ReadOnlyObj = {
+foo: string
};
and:
type ReadOnlyObj = $ReadOnly<{
foo: string
}>;
are equivalent.
This is handy when we want to redefine all the properties of an object type as being read-only.
For example, we can write the following to define a read-only type and object:
type Person = {
name: string,
age: number,
};
type ReadOnlyPerson = $ReadOnly<Person>;
let person: ReadOnlyPerson = {
name: 'Joe',
age: 10
}
Then when we try to reassign a property in the person
object as follows:
person.name = 'Bob';
We’ll get an error.
$Exact<T>
$Exact<T>
let us create an exact type from an object type.
This means that:
type ExactPerson = $Exact<{name: string}>;
is the same as:
type ExactPerson = {| name: string |};
$Diff<A, B>
$Diff<A, B>
creates a type with the properties that’s in A
but not in B
, where both of them are object types. It’s the same as A \ B
, which is the set difference between A
and B
.
For example, if we have 2 types:
type Person = {
name: string,
age: number,
};
type Age = { age: number };
Then we can create an object type which accepts the name
property with:
type Name = $Diff<Person, Age>;
When we create an object with the Name
type, we require the name
property:
let name: Name = { name: 'Mary' };
If the name
property isn’t included in the object:
let foo: Name = { age: 10 };
Then we’ll get an error.
$Rest<A, B>
$Rest<A, B>
is similar to $Diff<A, B>
but the property check is done at run-time. It’s the properties that are part of the rest operator.
It results in the properties of A
‘s own properties that aren’t own properties of B
. In Flow, exact object types are treated as having own properties.
For example, we can define a new object type with $Rest<A, B>
by running:
type Person = { name: string, age: number };
const person: Person = {name: 'Jon', age: 42};
const {age, ...rest} = person;
(name: $Rest<Person, {|age: number|}>);
Then the name
object can’t have a property age
anymore since we casted it to the $Rest<Person, {|age: number|}>
type. $Rest<Person, {|age: number|}>
returns a new type with the age
property removed from the Person
type.
$PropertyType<T, k>
$PropertyType<T, k>
gets the type of key k
from type T
. Key k
is a string. For example, if we create the Person
type as follows:
type Person = { name: string, age: number };
Then $PropertyType<Person, ‘name’>
will get us the string
type:
let foo: $PropertyType<Person, 'name'> = 'name';
Nested lookups also work. For instance, we can write:
type Person = {
name: {
firstName: string,
lastName: string
},
age: number
};let foo: <$PropertyType<$PropertyType<Person, 'name'>, 'firstName'> = 'name';
$ElementType<T, K>
$ElementType<T, K>
is a type that represents every element in an array, tuple, or object type T
that matches the key name K
.
For example, if we have the following type:
type Person = {
name: string,
age: number
};
Then we can use it to get the type for the name
property by writing:
$ElementType<Person, 'name'>
And then we can use it to annotate the type of other variables:
let foo: $ElementType<Person, 'name'> = 'name';
We can use it with tuples as follows:
type Tuple = \[boolean, string\];
let foo: $ElementType<Tuple, 0> = true;
With dynamic object types, we can pass in the type of the key directly to the second argument. For instance, if we have the following type:
type DynamicObj = { \[key: string\]: number };
Then we can get the type of the property keys with $ElementType
by writing:
let x: $ElementType<DynamicObj, string> = 1;
For arrays, we can write the following:
type StrArrayObj = {
strings: Array<string>,
};
let x: $ElementType<$ElementType<StrArrayObj, 'strings'>, number> = 'abc';
The code above works as follows. $ElementType<StrArrayObj, ‘strings’>
gets us the type for the strings
property of StrArrayObj
. Then we apply $ElementType
with $ElementType<StrArrayObj, ‘strings’>
, and number
to get the type of the array elements of strings
.
The number
is for retrieving the index of the strings
array.
Flow has many useful utility types which make creating new types easier. We can get the keys of an object to create a new union type. Also, we can get the types of values and create a new union type from them.
Also, there’s a type to change all the properties to read-only. We can also get the types of object keys, tuples or array entries.