Flow — JavaScript Type Checking
Type checking can save a ton of debugging time and help maintain a large code base very well, so if you aren’t very familiar with it, then now’s your chance to help your future self. The problem with JavaScript is that it is a dynamically typed language. Dynamically typed languages do not require the type of variable (string, integer, etc) to be known at compile time. This can result in errors that won’t get caught until runtime and difficult to find bugs. Other programming languages (Java for example) are statically typed languages which do define the type of the variable within the code.
As these problems from using a dynamically typed language have arisen, there have been some ways of adding type checking to JavaScript. TypeScript has long been the main contender, but we’ll be talking about Flow here (Flow Type works better for google results).
Why Flow
There are a few reasons that I’d like to highlight for Flow:
Type checking is opt-in
- We can check only the files we want to which allows Flow to be added to existing code bases very easily
Import checking
- Flow will catch import errors when we accidentally bring in an unknown property
No changes to pipeline
- Works very well with babel, React, etc
Getting Started with Flow
Check out the official getting started here which uses the command line and npm for testing. There is also a linter plugin for Atom that you can add in. Finally there is a typecheck plugin that can be added to babel here. Pick whatever option works best for your workflow. I have found the Atom plugin to be a bit of a cpu drain and as such would recommend the babel plugin.
Flow in Action
The first step is to tell Flow that we want it to check our file. All this entails is adding in @flow at the top of the file in a comment. Only the first example code below will display this.
Flow can automatically check types and is quite smart about inferring what we intend. The example code below will return an error due to Flow’s automatic type check. Flow is assuming that since we are multiplying our parameter by an integer we must want our input to be a number as well.
// @flow
function timesFour(x) {
return x * 4;
}
timesFour('Not a number');
Let’s say we didn’t want to trust Flow’s type check, we can also define what the type of our inputs and outputs are by using : type. The example code below defines our x variable to be a string, the y to be a number, and the output of the function to be a number.
function tryMyOutput(x: string, y: number): number {
return x.length * y;
}
tryMyOutput('Can it handle all of this', "hello");Arrays can also be defined to be full of only one type using flow. We will set this type similar to above using : Array<type>. The example below is expecting an array full of numbers and as such will result in an error.
function printSomeNumbers(numbers: Array<number>) {
for (var i = 0; i < numbers.length; i++) {
console.log(numbers[i]);
}
}
printSomeNumbers([9, 7, 'Three']);One final example of Flow’s ability is how intelligently it can handle null. In the example below Flow will not result in an error because it can see that the function has a check built in for null, so it’ll run fine. If we removed this check and instead just returned x.length, then it would return an error.
function length(x) {
if (x !== null) {
return x.length;
} else {
return 0;
}
}
var total = length('Hello') + length(null);Conclusion
With Flow there is very little overhead to adding in type checking to your JavaScript code and plenty to be gained. At it’s most basic, you can just add in @flow and utilize the automatic type inference. If you want to really make good use of it though, then start defining your types. Not only will Flow be able to catch type errors but it’ll result in cleaner code as the expectations of your parameters will be explicitly stated. Regardless of how much you use Flow, its so simple there’s little reason not to; so go write some clean code that has some type checking in it!