Categories
Flow JavaScript

JavaScript Type Checking with Flow — Utility Types

Spread the love

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.

Leave a Reply

Your email address will not be published.

If you like the content of this blog, subscribe to my email list to get exclusive articles not available to anyone else.