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 data types supported by Flow for type checking, including variables and basic properties of functions.
Variable Types
Just like in JavaScript, we can declare variables and constants with Flow with the let
, var
, and const
keywords respectively.
let
and var
are used to declare variables. This means that their values can be reassigned after the initial assignment.
const
The const
keyword is for declaring constants. Once it’s assigned a value, it can have another value reassigned to it.
Flow can infer the data type constants without adding type annotation:
const a = 1;
It’ll know that a
is a number.
Of course, we can annotate it with a type as follows:
const a: number = 1;
var and let
var
and let
are both for assigning to variables. The difference is that var
isn’t block scoped while let
is.
For example, we can write:
if (true){
var x = 1;
}
console.log(x);
However, if we replace var
with let
, we’ll get an error:
if (true){
let x = 1;
}
console.log(x);
We can reassign variables declared with var
or let
with another value. For example, we can write:
var x = 1;
x = 2;let y = 1;
y = 2;
Of course, if a data type annotation is added to a variable, then we must have a value with a compatible type assigned to it. For example, if we have a number variable:
let y: number = 1;
Then writing:
y = '2';
after it will create an error.
If a data type annotation isn’t added to a variable or constant, Flow will assume that the type of the variable or constant is the union of all the types of the data that had been assigned to it.
For example, if we have:
let a = 1;
a = 'foo';
a = true
Then we can write:
let b: number|string|boolean = a;
without getting any errors. This is because we assigned a
a number, string and boolean before we assigned a
to b
, so a
is assumed to have a union of all those types.
Flow can also figure out the type given that it’s been assigned. For example, we can write:
let foo = 1;
let num: number = foo;foo = false;
let boo: boolean = foo;foo = "foo";
let str: string = foo;
However, code that’s run within blocks like functions and conditions will throw off Flow’s type inference and will throw an error when we try to assign it to something that we expect to work.
For example, if we write:
let foo = 1;if(true) {
foo = true;
foo = "foo";
}
let str: string = foo;
We’ll get the error:
[Flow] Cannot assign `foo` to `str` because number [1] is incompatible with string [2].
since Flow doesn’t know the type of the foo
variable.
Likewise, Flow can’t figure out what type foo
has after it’s been reassigned values inside a function as follows:
let foo = 1;const fn = ()=> {
foo = true;
foo = "foo";
}
fn();
let str: string = foo;
Function Types
A function can have type annotations for its parameters and the return type of the function.
For example, if we have the function:
function add(a: number, b: number): number {
return a + b;
}
Then the number
in a: number
and b: number
are the parameters’ data types, and the number
after the close parentheses is the return type of the function add
.
Once we annotate the parameters with types, Flow will validate the types when anything is passed in.
For example, if we have:
add(1, 2);
Then Flow will accept the code as valid since we passed in numbers as indicated in the function definition.
On the other hand, if we pass in data of any other type like string as follows:
add('a', 'b');
Then we get the error:
[Flow] Cannot call `add` with `'b'` bound to `b` because string [1] is incompatible with number [2].
Since we passed in strings for the arguments, which aren’t compatible with numbers.
Function Syntax
Flow’s function syntax is similar to JavaScript but added extra type annotations to the parameters and return type.
We can have an infinite number of parameters with the rest operator, which is indicated with the ...
operator and holds the arguments that are excess of the listed parameters as an array.
For example, we can write:
function foo(str: string, bool?: boolean, ...nums: Array<number>): void {
console.log(nums);
}
foo('a', false, 1, 2, 3);
Then we get [1,2,3]
for nums
.
However, if we pass in something invalid as follows:
foo('a', false, true, 2, 3);
Then we get an error for passing in true
into the third argument since we specified that ...nums
is a number array.
We can declare variables and constants like JavaScript with Flow. In addition to JavaScript’s syntax, we can add data type annotations to variables and constants.
Function syntax and definition also extends from JavaScript’s syntax. Like variables, we can add data type annotations to variables, including ones operated on by the rest operator, and also add a return type annotation to a function.