Categories
Flow JavaScript

JavaScript Type Checking with Flow — Arrays and Tuples

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 how to add Flow types to arrays and tuples.

Array Definition

We define arrays exactly like in JavaScript. To define an array, we can use the literal:

let arr = [1, 2, 3];

We can also use the Array constructor as follows:

new Array(3);

Array constructor with one argument will create an empty array with the given length, so new Array(3) returns:

[empty × 3]

Calling the Array constructor with more than one argument creates an array with the arguments as the content, so:

new Array('foo', 'bar')

will get:

["foo", "bar"]

We can also add array entries after it’s defined:

let arr = [];  
arr[0] = 'foo';

Array Type

We can type arrays by using the Array<Type> syntax, where Type is any type that we want the array entries to be.

For example, we can define a number array by writing:

let arr: Array<number> = [100, 200];

We can type an array with different types of values by using the mixed type as the type. For instance, we can write:

let arr: Array<mixed> = [1, true, "foo"];

Array Type Shorthand

We can use the Type[] syntax as a shorthand for typing array. For example, we can write:

let arr: number[] = [100, 200];

to declare a number array.

We can declare an array that can be null by putting a question mark before the type name. For example, we can write:

let arr: ?number[] = null;

Also, we can assign a number array to it:

let arr: ?number[] = [1, 2];

If we want an array to take on null values in addition to values with the given type, we can put parentheses around the type name. For example, if we want a numerical array that can have null entries, we can write:

let arr: (?number)[] = [1, 2, null];

Accessing Array Values

Flow doesn’t check whether an array entry is defined before we access it. Therefore, we can run into undefined values at runtime, which may cause errors.

For instance, we can write:

let arr: number[] = [1, 2];  
let num = arr[3];

We should check if entries are undefined before accessing them.

Read Only Arrays

We can define read-only arrays by writing $ReadOnly<T> , where T is the placeholder for the type that we want the array values to take on.

For example, we can define a numerical read-only array by writing:

const arr: $ReadOnlyArray<number> = [100, 200];

We can only read the values from the arr array.

Also, read-only arrays can take on object types. For example, we can write:

const arr: $ReadOnlyArray<{foo: number}> = [{foo: 1}, {foo: 2}];

In the case of object entries, we can modify the property value of the object’s existing properties as follows:

arr[0].foo = 2

Note that read-only arrays have subtypes of a union type. For example, we $ReadOnlyArray<number> is a subtype of $ReadOnlyArray<number | string> .

However, Array<number> isn’t a subtype of Array<number | string> .

For example, we can write:

const foo = (arr: $ReadOnlyArray<number | string>) => {  
    
}const array: Array<number> = [1]  
foo(array)

In the code above, array will be converted to a $ReadOnlyArray<number | string> as it’s passed in to foo . This is because read-only arrays can’t have its entries modified to types that aren’t specified, so Flow allows it to be passed in.

Tuples

Tuples are lists with a small number of items. In JavaScript, we create a tuple with arrays.

With Flow, we have a tuple type to type these kinds of arrays. For example, we can write:

let tuple: [string, boolean] = ['foo', true];

Then the first entry always is a string and the second is always a boolean.

We can access entries by its index, so given the tuple that we have above, we can write:

let str : string  = tuple[0];

We can’t access indexes that aren’t defined in the tuple. For example, the following code will fail:

let str : string  = tuple[3];

since we don’t have 4 or more elements in our tuple.

Flow doesn’t know the type of value we try to access, so whatever the value is will have the union of all the types in the tuple.

For example, the value of tuple we have above will be of type string | boolean :

let val: string | boolean = tuple[0];

When we set new values to a tuple entry, it must match the type of that’s defined in that location. For example, if we have:

let tuple: [number, boolean, string] = [1, false, "foo"];

Then whatever we assign to tuple[0] must be a number, tuple[1] must be a boolean, and tuple[2] must be a string.

Strictly Enforced Length

Lengths of tuples are strictly enforced, so we can assign a tuple of one length to one with different length.

For example, the following assignment will fails since tuple1 and tuple2 have different lengths:

let tuple1: [number, boolean, string] = [1, true, 'foo'];  
let tuple2: [number, boolean] = tuple1;

Tuples aren’t Arrays

In Flow, tuples aren’t arrays even though they look the same. We can’t use arrays methods with tuples and we can’t assign arrays to tuples regardless of length.

For example, the following will fail:

let array: number[] = [10, 20];  
let tuple: [number, number] = array;

Assigning tuples to arrays also doesn’t work. For example:

let tuple: [number, number] = [1, 2];  
let arr: number[] = tuple;

will fail.

Array methods like join or push can’t be used on tuples, so:

tuple.push(3);  
tuple.join(',');

will give errors.

Arrays and tuples are lists of items. They aren’t the same. Arrays can use array methods. Also, we specify the type for all entries at once. With tuples, we define the type for each entry. Tuples also can’t use array methods.

Arrays can have null entries if we add a question mark before the type name and parentheses around it. An array can be null if we skip the parentheses and keep the rest.

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.