!= The Fundamentals of Flow in 10-ish Minutes - Sessions by Pusher

The Fundamentals of Flow in 10-ish Minutes

Alex Booker speaking at London Node User Group in October, 2016
67Views
 
Great talks, fired to your inbox 👌
No junk, no spam, just great talks. Unsubscribe any time.

About this talk

Flow is a static type checker for JavaScript (an otherwise dynamically-typed language). You can think of it as a productivity tool that catches common errors before they run. Take the infamous "'Undefined' is not a function" error as an example. When writing vanilla JavaScript, you would have to wait until runtime to receive this arduous error. Flow - on the other hand - integrates with your favourite editor and analyses the correctness of your code as you write it. If you were to reference a non-existent function, Flow would not only tell you immediately but also provide additional context that makes the error easier to fix. Whilst Flow is predominantly used with front-end technologies like React, I have been using it with great success for my Node.js projects and in this talk, I'll not only show you how to get up and running with Flow, I'll also share my experience with you. I'll also take a moment at the end of my talk to touch on the differences between Flow and TypeScript.


Transcript


[00:00] Hello everybody. My name is Alex Booker. I'm a developer advocate of Pusher. Pusher, for those of you that don't know, is a hosted platform that makes it really easy to build real-time features into your application. I can probably talk for hours about Pusher, but I'm not here today to talk about Pusher. I'm here to talk about another technology I'm excited about called Flow Type. [00:18] If you look into Flow Type at all, one of the first things you'll read is that Flow is a static-type checker for JavaScript. What's this, is totally true in my mind, and the way I like to explain Flow is as a productivity tool for JavaScript developers like you and I. [00:32] You see, Flow is a tool that you download locally, and run on your new or existing code bases. It develops a wealth of information about your code using static analysis, and the text areas or potential areas before you run your code. [00:43] I think one of the best ways to get started with Flow, and to get to speed, is to look at a little bit of code using Flow. What I have here is a function called area that can calculate the area of a circle given its radius. Here I'm going to call it with the number 10. [00:56] Bear in mind, we're passing a string here, not an actual number. You might expect, in this case, JavaScript will convert the type to a number automatically. That is true, by the way. However, in this particular case, I'm being a little bit tricky, because that's not a zero. It's a capital letter "O". [01:11] [laughter] Alex: [01:11] Therefore, when you run this code, it's probably not going to behave how you expect. The reason why I'm tricky like this is, because it's essentially showing how implicit type conversions usually lead to bugs. Usually, if you ran this code, you'd get an error runtime, depending on how you use the results. I think it returns none actually. [01:26] If you install Flow, and you very simply add a header comment to your code file where you specify that you want Flow to process it, you'll get an error right in your edit that tells you that the type you're providing is incompatible with the area function. [01:38] The way Flow knows this is, because only simply adding that Flow header comment is, because it analyzes the code. It analyzes it and builds a context about it. In this case, Flow knows that math.pi is a constant. That it's a number. It makes no sense to multiply a string with a number. Although, some people might say it does make sense in JavaScript, it's generally not advised that you do so. [01:58] Let's look at another example. This here is a function called length, that simply returns the length of the given value. I'm going to be a bit tricky again, and just pass null. Obviously, this code is going to blow up, because you can't access a property on null. But, why should you wait until runtime to see this error when with Flow, you can see that error right in your editor right away? [02:16] In this case as well, if I just go back one slide, you can see the error, and you can fix it very quickly. [02:21] The reason why I put this slide here is, because I want to show that Flow enables you to be more productive by simply providing you the area in your editor. You can then very quickly go and fix it. In this case, I fixed it by checking for nullity before accessing the value. Of course, there are other kinds of fixes. [02:34] These examples are completely contrived. You could probably figure out that they're going to blow up just by looking at the code. However, I'm using contrived examples just to illustrate Flow very simply. I'm sure you can all relate to experiences as JavaScript developers, where you've had some kind of bug that is stemmed from implicit-type conversions or nullity. [02:49] In the previous example, I passed null. It was just to be clear you can imagine where this null value came from a function call, maybe in a third-party module, or could've been your own code base, maybe 10 files deep. In big code bases, Flow really proves its worth. It becomes more valuable as it helps you find bugs easier, and avoid writing them all in the first place. [03:07] Flow gets more interesting yet, when you try and export a module or a function. In this case, I have the same length function. I'm exporting it at the bottom, there. In JavaScript, in general, when you export something, you're essentially making it public. [03:18] When you export something using Flow, you have to explicitly define the types of the arguments, as well as the return type, and using Flow, you can do that very simply using a colon and then the type. [03:28] What we're specifying here is for the length function will only take a string, and we're also specifying that it returns a number. When you import that file, or that function in your own projects, if you pass anything but a string, Flow will complain. If you try and return anything but a number, Flow will also complain. [03:43] Providing this additional information can also power auto-completion and other good tooling. The thing is, people tend to like JavaScript, because it's very nimble, and therefore great for prototyping projects. In the short-term, adding type annotations actually slows you down, in my opinion. [03:57] However, in the long-term, it powers really powerful auto-completion in your favorite editors. Because, whilst Flow has a CLI, there are also extensions for all of your favorite editors ranging from Vim to Atom. [04:07] What's more, when you use type annotations, it makes your code more readable, because you're being very clear about your intents. Someone skipping through your code file can look at the name as well as the type annotations, and very quickly figure out what the function takes, what it returns, and what's its main purpose is. [04:20] You can also use Flow type annotations to generate documentation in some cases. It won't be the best documentation, but it's great for API docs, where you can go and fill in additional context later. [04:29] In my experience, with types and Flow, in general, is that your code is more likely to work if it compiles. If your code has no Flow errors, it's not guaranteed to run successfully. There are still runtime errors, of course, but it's more likely that your code will work. [04:41] What about third-party modules like Express? Because, when you're writing this code in your own projects, it's easy to add type annotations to everything, but you have no control of the third-party modules. [04:49] If you look at the Express source code, there's no notion of Flow annotations, let alone Flow. How is it that Flow can, for example, provide auto-completion for Express, or to detect areas when you provide invalid values? [05:00] Despite this being my first talk, I'm going to attempt some live coding. We'll see how that goes. If you want to use Flow in your applications, the first thing you probably want to do is install the Flow binary, which you can do using your favorite package manager. [05:10] [laughter] Alex: [05:10] Recently, Facebook released a new package manager called Yarn, which you might have seen the previous speaker using. It's very, very similar. If you see me using Yarn, it's essentially the same as using NPM. [05:21] If I want to install Flow globally, I can cite yarn add and then flow bin. What this is going to do, very simply, is it's going to enable me to access Flow from my command line. I bumped that font up a little bit as well, for you guys. [05:32] That will enable me to run Flow on my project. It also allows me to run Flow innate, which very simply creates a .flow config file. That's all it does. If you look at the contents of Flow config, it's essentially empty. However, it's important to note that in order to use Flow, this .flow config file has to exist. [05:48] What kind of I want to do, just to illustrate Flow in action, is I want to drop into Atom and create a new file. I'll call this guy index.js. I'll zoom in quite a bit, as well. Say, I'm building an express js server, I might come here and I'll write const express = require('express'). [06:02] In this particular case, I haven't yet installed Express. This code is erroneous. If I was to run it, I would certainly get a runtime error. If I simply add the Flow header comment, now you can see that Flow has kicked in, within my editor. It's detected, but Express has not been installed. [06:15] In addition, you remember me earlier saying that Flow is not just about types. It's just generally about productivity. It can detect other kinds of common areas as well, which you might find if you, for example, install or remove a module, this comes in really, really handy. [06:26] To fix the error...And by the way, you can also run Flow from the command line. It just so happens that my editor plugin's kicking in, because it's generally better to see those errors in your editor. I can run Yarn add Express. It's just the same as writing NPM install Express. When I run Flow again, the error is no longer there. [06:42] The thing is -- and the error does still appear in the editor until I save the file -- there is no longer an error. Say I move along and I, for example, create the Express application. I want to create a roots handler for particular roots, and just say I'm going to be nuts, and I put way too many Ts. [06:55] Clearly, that number doesn't exist. It's not valid JavaScript, and yet Flow isn't complaining. That is simply because Flow does not yet have enough information about Express to tell us what members are available, what members are not, and that's where these definition files come in really, really handy. [07:10] Essentially, what the community do is, when they find an open source library like Express that is lacking types, they create these separate definition files, but don't contain any application logic. They just describe the public interface of the NPM module. [07:23] There is one for Express. By the way, what the community tend to do is contribute all of these to a centralized repository called Flow Type. There is a tool built around Flow Type that makes them really easy to install. [07:33] I need to install Flow Type first, you notice I'm inside the Yarn, Global Add Flow Type, which again, is the same as running NPM install Flow Type. I should have just used NPM, right? But, Yarn supports offline mode. So, if the Internet did give it out, I could have utilized Yarn for that reason. If you haven't looked into Yarn yet, you might want to. It's quite cool, by the way. [07:50] In any case, in addition to installing Flow Type, you also need to install flow-bin locally, as a devDependency. Rather, Yarn add flow-bin as a devDependency. This is so that, Flow Type can check what version of Flow you're using locally, for obvious reasons. [08:07] Now, what I can do is, I can write Yarn, or rather Flow Type install. Bear in mind, I've just installed Flow Type. I can run Flow Type install, and what this will do is it will look at your local packages and it will try and pull in definition files for them. [08:19] I installed flow-bin locally. It's brung down bot definition file, even though I don't need it. I'm also using Express 4, which is lib definition file down 4. Now, having done that, if I save the file in Atom, the error should kick in. [08:33] What's more, if I run the code in Flow, the area kicks in, because now this definition file exists, it's possible for Flow to look at what members are available, get() is not available. I can go in there now, and fix that if I want to. [08:42] Things get a little bit more powerful yet, because say, for example, I want to add in Lodash. I can just write Yarn add Lodash. Then, I simply write Flow Type install. It's a really nice workflow, whereby you bring down your NPM modules, and then the types. It's not inconceivable that you can set up some kind of hook to do this automatically. In fact, I would encourage it. [08:59] I could come here, and I could say const _ = require('lodash'). [09:03] What's really nice is that, when I bring in Lodash and I type _ and then dots, you can see that I've got a list of all of the possible functions here, which is great for API discoverability, despite, I think, George laughing of nobody here using .NET. [09:14] I come from a .NET background. I really appreciate types. One of my favorite things about languages like .NET and Java is the ability to discover an API within your editor. Now, I have that back in Flow, which is awesome. [09:25] For example, if I want to call the min function, which is you probably can infer returns the minimum value in an array, I can pass an array. This will return the minimum value in the array. [09:34] The reason why I do this is, because there's something about Flow that kind of blows my mind, that I find to be so powerful, which is that if I type this variable to be a number, it's going to work, right? Because, min is obviously going to return a number. If you provide an array of numbers, Flow type, or rather min, is going to return a number, because that's all the cameras heard. [09:50] If I pass an array of strings, be it a number inside of a string, it doesn't matter. Flow's immediately going to kick in, and complain that it cannot take the value that min returns, and store it as a number, because it's clearly going to be a string. [10:00] The way that Flow kind of analyzes your code built in this context, and dynamically provides new areas without you having to really change much about your code, in my opinion, is very powerful. [10:09] Whilst I previously touched on the fact that with Flow Type, and types in general, as you have to add these annotations, it becomes a bit more cumbersome than simply writing JavaScript dynamically. It's actually possible for Flow to infer the type of an argument based on its right-hand side, or the right-hand side of the assignment, I should say. [10:25] In this case, if I type result and then dots, you can see I get a bunch of auto-completion options for members on this string type, because results, whether we specified it explicitly or not, is considered by Flow to be a string. [10:37] If I then change this to, for example again, return a number simply through virtue of passing an array of numbers. I type result dots, it's going to give us auto-completion based on that being a number. We didn't have to come here and say that it's a number, but Flow has managed to figure it out. [10:51] The one last thing I wanted to mention Flow is that, for example, if you look at this code, it's completely valid. Flow is not complaining. If I drop into the terminal, I run flow, there are no errors. [11:00] However, if we try and run this code inside of Node, it's going to error out, which is ironic, because Flow is meant to prevent runtime errors. The trouble here is, that whilst Flow understands this syntax where you have a colon and then the type, the Node runtime, the V8 engine simply does not understand this, because it's not a part of the JavaScript's spec. [11:15] There are a couple of ways around this. The first of which is that you could surround the type annotation in the comments. As far as Flow is concerned, it recognizes that to be a type still, and will therefore give you type errors depending on the type that you specify. [11:26] However, when you run your code inside of Node, or any other environment for that matter, it's going to interpret it like a comment. It's going to run fine. This is a little bit icky. [11:35] What's more, I should mention that I'm being incredibly superficial in this talk. My goal is to get people excited about Flow Type, and to show what it can do. I don't know a huge amount about it. What's more, I'm not touching on many of the advanced features, which means that there are some more advanced features I'm not showing you here, that simply would not work with comments. [11:50] The other solution, which I'm going to drop into my slides for, because it would be a bit cumbersome to demo live, is that you can take your annotated Flow code, you can pass that into Babel, which for those of you who don't know, is a transformation tool for JavaScript that takes one input and outputs in a different format. [12:03] You input that original Flow code into Babel, with a special plug-in designed to strip the types from Flow. After running your code through Babel, you get the sort of vanilla JavaScript output that you can then feed into Node.js, or any runtime, and that will work just fine. [12:17] That has been my short introduction to Flow Type. Thank you very much for your time. [12:20] [applause]