An Introduction to Flow

Benjamin Reid speaking at Bristol JS in March, 2017
1865Views
 
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. In this talk we'll run through how and why you should try out type checking and Flow specifically, and then dive into some of the other benefits and side effects you get from using it.


Transcript


Hello, well, my talk is not gonna be as exciting as hacking dinosaurs that I just saw in there, I'm actually really excited to see that talk. So this is gonna be a little dryer than that, there's gonna be no dinosaurs, sadly, so this talk is about Flow, so what is Flow, so Flow is a static type checker for JavaScript that's made by Facebook. And if anyone doesn't know what static type checker is, I didn't really know much about type checking before I got dug into this other than a bit with AS3 stuff back in the day, but this is from Wikipedia, static type checking is the process of verifying the type safety of a programme based on an analysis of a program's text source code, and that's kind of the nice key bit, but in English, for me, it's kind of, it eliminates, slash reduces bugs in your code before you end up running them. So I'm kind of gonna go, like, a quick demo of kind of how to get up and running with Flow just to kind of show the simplicity of it and then talk a bit about the benefits and a few other little demos and kind of how I found it in a product we've got in nearly production, so I've been working on it for about three months so I've kind of had some rough experience with it in production and how it's kind of benefited that project and the teething stuff around it, so first, yeah, it's a node package, isn't everything at the moment? So you can instal it pretty easily, and I'm gonna just give a quick demo, just that setup running on a very simple JavaScript file. It's probably worth just showing the file beforehand. So this is kind of like a plain JavaScript file minus that little comment. And if I go to my terminal, maybe a big bigger. I'm gonna run a slightly different command because I have lots of different Flow files in my project but if hello-world was the only file, it would kind of be the same, it's just running Flow, when I run this here, you get this kind of nice, well, you get type checking, just kind of out of the box and that's kind of it in its simplest form, it's kind of adding some annotation over the top of your, you know, bog standard JavaScript so it can go in any project that has JavaScript. But if we come back to the example just quickly, you can actually kind of remove the kind of type declaration, so your code will probably start off looking like this and then Flow kind of still works and it can kind of tell what's going on, so if I run that again, while it's not as good, you're kind of missing the really explicit stuff that you kind of set before, it's still clever enough to infer what was going wrong, so even without you doing any of the type annotations yourself, it just kind of works out of the box which is really nice, all right, let's just revert that back a bit. So obviously you're probably not gonna be running that from the command line every time you wanna do type checking because you'd be there all day, so my actual favourite way to run it is with something called hope I pronounce this right, Nuclide, has anyone heard of it? Couple of people, so when I saw this, I actually thought it was a completely different editor, unbeknownst to the fact that it's a package that just runs over the top and it kind of turns it into a halfway house between being an ID and still being a text editor which is why I'm still kind of okay with it. And again, it's also by Facebook. So we'll kind of do that kind of same example. Let's run this one. So while this is a bit small, can everyone kind of read that yeah, just about, so rather than having to run on the command line every time, you kind of pop Nuclide into Atom and you have to do a little bit of kind of configuration, you kind of have a flow config, this is just kind of, that's what comes from the flow init command, which has got nothing in it at the moment. But you kind of, so once you've got Nuclide installed, you have to kind of do a little bit of fiddling, once you've installed node, you kind of have to say, make sure you look at this version if you're working between different versions of node NVM or something, but now, this kind of extra panel that I've got here is kind of checking the code throughout my entire project so you can kind of see down here as well, there's kind of 11 errors and a couple of warnings because it's kind of scanning the entire thing and if I kind of tick this off it kind of shows me across all the different files where everything is going a bit wrong. So that's my kind of preferred way to run it, so integrating into an existing project where, you know, you're probably maybe already coding it and being able to just kind of drop a package in and get it running and have it do the automatic type checking across your entire project is really good. So I'm just gonna go through some of the just kind of really simple kind of type annotations you can do. And there are absolutely tonnes on the Flow website for various things you can check but these are the kind of ones that you'll probably use the most or you'll kind of use from day to day. So this is almost kind of nearly just plain JavaScript, the only thing that's really changed is kind of the colon after kind of variable declaration and then the kind of defining what these programmes should be, and just for an example, just to see it in practise, if I tell this, this should be a string, you can see my arrow pops up straightaway and I can come in here and make it a nice little handy arrow that tells me where it's gone wrong and in this simple example, it's all a bit contrived, it's probably like, well, why would you wanna do this but once you get a bit further into this you see some of the benefits that kind of come out of it so you can test for all these different parameters, but it definitely becomes a bit more interesting when you're kind of getting to the more complex stuff but being able to test those simple things, those simple processes in JavaScript as everyone probably knows if they've tried to do some sums and somehow a string has slipped in there and all of a sudden you can calculate the string instead of doing addition, you know, everyone's probably experienced that, this in theory will stop you from doing that, so the next thing, arrays. You may have noticed I've gone for giant emojis in this talk, they're kind of weird really big, aren't they, that caterpillar at the start, it's a bit scary. So arrays. So this is quite nice, slightly different, you can actually define an array without this kind of extra bit on here, but it kind of says this is an array and this is what the array should contain and here, it's just been chopped off, you can kind of say this array should contain numbers or strings so it kind of lets that through, now on line seven, I'm kind of assigning it again and it's throwing an error because it's like, hmm, I'm kind of expecting this to be a number but this could possibly be a number or a string so it's kind of, like, right, how do you solve that? And again, this is a very contrived example. Let me zoom out a bit so you can kind of see that so now I'm checking to see if it is a number before it kind of gets assigned so you'll probably never write that bit of code, you know, you probably have something else that's kind of wrapping that kind of assignment up but being able to test that, particularly kind of over large scale applications, even that simple example you'll see a lot and that's saved me from having errors being run in the browser or actually being run in React Native because I'm doing a lot of React Native work at the moment and one simple JavaScript error can kind of throw the entire application to crash, so having the kind of protection in there is pretty handy. So functions, again, something that everyone will write lots and lots of times. So here we can see on kind of like an argument, so I can see here, when I'm trying to log a number, I'm logging a string and you get a nice, say, hey, this isn't actually what I was kind of expecting, so you can come through and kind of fix that and that makes that happy and you might kind of see a lot of this in third party libraries because one of the really nice things I've found about it is particularly with the kind of function declaration for arguments, is if you're using a library, you know, that's got a variety of functions and they actually normally take, you know, they'll take a series of arguments and you're kind of, like, well, what order were they in, you know, what types were these libraries expecting, and you normally have to go back to the documentation, normally, well, some libraries have, like, an object and those have got, like, named keys but when you've got this kind of string of arguments and you're not sure what order they go in and what they have to be, adding this flow, this kind of annotation, allows the libraries to kind of self document so I've found with similar libraries that have Flow support, rather than me having to go to the documentation, I can use the code and my little panel down here will just say, hey, you're kind of missing something, you're passing in the wrong type and it saves me having to go back to the browser, read the docs and come back in and it's all just kind of in Atom itself. Another nice feature is kind of this, this shows some code which will never be reached, I haven't actually found much use for this at the moment when you've completely kind of screwed it up, but the function here is saying I'm expecting a message and it's also expecting an optional string so this is how you can kind of do kind of optional parameters, and if we actually remove the console log, actually remove this as well, it would then kind of freak out because it's, like, hmm, the string could in theory be null and I've actually said this function should always return a string, so that's the kind of annotation that you would use for kind of saying I want this function to return this particular type. So Flow actually then forces you to put an if statement around it because if not, it's gonna complain. And if I just fix that off, we'll just return, hello Bristol And get rid of that and that becomes happy. Let's just fix this one as well, so that's kind of function arguments and kind of what the returned value should be, it's also probably worth mentioning, you can do the ES6 style kind of, like, default value as well which I believe, if I can remember correctly, is that, so you can still have the nice ES6 style function arguments in there. So next, classes, anyone using ES6 at the moment? Yeah, quite a lot of you so you've obviously probably been using the new kind of ES6 style classes and this has some really nice kind of support for ES6 classes, so any kind of instance variables you define, so see where we make this new pizza, we can actually say, the kind of annotation here is kind of a bit weird, but it's kind of saying the base kind of instance variable of a pizza is a string, and it has to equal tomato or barbecue. But at the moment here, I've just got this tastypizza variable, should be an instance of my pizza so you can also kind of do type annotations for instances of your own classes as well, so this is freaking out at the moment because it's only a string and I actually tried to make the variable be an emoji but Flow actually said it was illegal. So if instead I do a new pizza and pass in the base as the first parameter, it's tomato, that then becomes happy, but if you wanted a cheese base, it's then gonna freak out, so when you are actually doing your own classes and having this kind of logic, and you know, you're passing this between your team and you've probably not got time to write documentation, this just self documents it and it's really powerful and it's kind of all selective and, you know, you can have a whole variety of combination of different things and then obviously you can mix in, you can annotate function arguments like you can in class and things like that, but the important thing to remember is defining the kind of instance variables at the top. Next, aliases, so aliases, again, is something that I have ended up using a lot of us so particularly when you're getting data from APIs or any function that tend to generate objects and stuff, so you can actually define a custom type so here I've got my type user which has got an ID which should be a number and a first name and last name, they should be a string, and down here we've got the name Malcolm, anyone can have a point if they can tell me where that's from, and currently that's invalid if I hit save, I've gotta satisfy my custom type and it becomes valid, and this can be super helpful, right, you end up with, like, in most of the project you've got, like, a types file which has got the list of all the various different types and it's got nice ES6 support so you can export types and you can share them between files which is super powerful when you're working with much larger scale projects So coming onto that is modules, these kind of come together almost, so here we've got a third party module which I am just gonna check to see that I definitely don't have this installed. Maybe I do, let's have a look, so the error I'm getting here is the required module could not be found and if I look in my modules folder, I've only got react in there, now bear in mind, hopefully I've still got some internet connexion, I was actually gonna do a bit of a blue Peter and just rename the modules, but it's kind of worked out okay, so now I wanna reset file, he says, now it's actually detected that that module is installed and again, while it seems kind of simple, we've had some people at work poking over portions of my code with the Flow code in it and they're going into a project which is, you know, into the thousands of files and instead of them kind of compiling it and going, oh, this module's missing or this function's missing, they've just got the error saying, you know, you're missing this library or you're missing this file that you forgot to copy across which may be a bit of a weird scenario but especially if you're new to the project or completely alien, this being able to highlight the kind of modules that are missing is really powerful especially if you're doing refactoring, I've been doing a lot of refactoring without having to worry too much that I'm gonna be missing files or having to kind of test it in a browser or kind of recompile it in react because it's just telling me that it's missing so in here, if I, so this is what I was talking about with exporting the types. So here I've defined a comment and you just export it like you would do any kind of normal kind of named export in ES6, and you import it slightly differently, you just have to stick this kind of type, type definition before it and now I can use the common type from another file in here so I can just quickly make this happy. And then that makes that file happy. Yeah, I mean, as I said, this thing is really powerful, being able to share this along a large scale project has been really advantageous for the work we've been doing. So third party code. So with something like Redux, I believe it actually has some kind of Flow typing in it because it's loosely related to kind of Facebook kind of sphere, like, ecosphere, but if you import a library, Flow can get real antsy about libraries that you import that don't have Flow types, you can plain out ignore them if you look in the Flow config, you can kind of just come in here and say, ignore this code, it happens quite a lot with React Native, a lot of its dependencies are completely negate of any Flow kind of annotation, even the native components are missing as well, so you can ignore them but they have come up with a nice ish solution which is called flow-typed, so it's kind of a community driven thing where people can actually specify what Flow refers to as a type definition, which I won't go into in any detail because it's all on the website but they essentially kind of recreate the API that these third party plugins provide or modules provide and instead of having to rewrite the entire kind of module, they just say, this function that this module exposes should expect this and it's a very simple little file but it means the community can, instead of badgering the author and making PRs because they might not wanna add Flow, you can still use these third party modules and still satisfy your Flow types. And I think I might have got one in here for the Redux, let's actually run that very quickly just to see if it works. And then I just want my Flow version, so yeah, another good point is your Flow version is very important, it's very important to Atom because you can store various different versions, it's also very important to Nuclide because again it looks in your Config and React Native actually pulls down its own Flow config so as me and Atom discovered, when you are running it here, it's kind of like a little spinner, you can see that very quickly, kind of down this corner which is kind of Flow working out its stuff and that can spin infinitely just if you've set the wrong Flow version, so it's worth kind of noting which Flow version do I have? Yeah, I've actually forgotten what my Flow version is and how to, oh, they do have it. Is that gonna be happy, so yeah, it looks up this libdef, and you can use the community versions or you can actually write your own local version so if you happen to have borrowed some colleague's code or you've got an internal module, anything like that that doesn't have it, you can actually write it specific to your project, you don't have to publish it or anything. But what that does is it sucks it down into MPM 'cause you can use it with Yarn if you're trendy. And this is what that file looks like, if I close that down, so ... What's a really slightly simple one, so the create store, if anyone's used Redux, they've probably used create store before. And this is just a definition, there's some kind of slightly more advanced things in here where they've got kind of dynamic typing so that kind of, the greater than and less than are kind of creating, like, dynamic type so you can see whatever gets passed into the functions gets returned but I haven't played very much of that, but essentially you can not worry about third party modules kind of mucking with your code. So the benefits of Flow for me have been that it can insert into any project at any point which is really important if you've got projects that have been hanging around for, you know, a few years and you've got existing clients and stuff and if you wanna just start playing around with it, with the type checking, you can just drop it into one file, you know, you should always leave the code better off, when you come to, you know, do a pull you should leave in a better state so you could, providing that the build chain kind of provides for it because obviously, you know, that type annotation's not gonna work in a browser, but we'll come on to kind of how you can actually use it kind of, you know, in the browser or wherever you want to actually use it. Yeah, so you can integrate it into legacy projects as I said Being able to do it in one place is really nice, you can slowly migrate it bit by bit, and for me, it actually has helped reduce bugs. 'Cause I hadn't done much of the kind of typed code as I said before, but being able to do these checks before running it inside the simulator or going through a build step or anything like that, you know, you get that kind of instantaneous feedback which just saves you time and it kind of gives you confidence that when you say this is what it should do that it's actually doing what you want it to do, it's a nice advantage to have in JavaScript. So yeah, how do you actually, a point that I haven't really kind of covered to any great detail because you can do it in so many different ways is how you turn that Flow code, 'cause obviously it's got those annotations which the browser is not gonna be happy with, most people'll be using Babel. And it kind of, I guess it's not a compiler, something about Flow, they kind of say, it doesn't compile it like something like TypeScript might do but it kind of just strips out all the Flow code so when you saw I was importing the kind of type from one file to the other, that just gets completely removed so it doesn't add anything to the payload, it doesn't kind of muck with your JavaScript, it's simply a Babel plugin which will remove the code, so it kind of, it never adds anything to it or anything like that, so that's a kind of upside to it. And again, self documenting, I don't really go into that in kind of any great detail. I might go into it a bit later but when you're actually using the code, being able to have, like, the function kind of, like, parameters come up, which is kind of semi there in Atom, I kind of would like to come to it so you can see here, it's kind of got the documentation in line and you can kind of, if you can deal with UI, you can actually pin it while you scroll around so there's that, which is getting more ID like, but it's kind of nice, I'm not a massive fan of it. It saves you from pressing command R a lot, I know my two fingers here are kind of happy about that. Yeah, and it leaves less to worry about when, you know, updating the code. So when to use it. Larger projects, it's kind of, I don't really wanna do another large scale JavaScript project without it because of the benefits that I've had, but you know, if you're mucking around, have an evening or, you know, a weekend or you're doing a one page, one line JavaScript file or a single file, there's probably no need to kind of use it unless you just want to have a play with it. When multiple people are touching the code which I really like because at Simpweb we have lots of people from different disciplines doing all different kinds of things and it's really helpful when people aren't quite familiar with JavaScript, and if they've got that extra bit there to kind of cushion some of the weird things that it might do or how APIs work, I think that's pretty invaluable, and if you anticipate refactoring, this is, again, I've mentioned before, but it's worth saying again, it just gives you confidence when you're refactoring that anything that you're gonna break is gonna be safe because Flow is gonna tell you about it before you go anywhere. I've almost got to the point where I've got, like, tested code with Jest and it almost slightly feels unnecessary because Flow is kind of protecting me already. And code you may come back to after a period of time. Again, this kind of plays back to documentation stuff. If you come back to it, like, in a month, you know, quite often tend to forget what the hell I was doing, probably a month ago with some code, but yeah, the team benefits, which I won't go over again, but it's really good in a team, so some bonus stuff, this is a couple of the nice things that I've found about Flow, it's kind of a semi side effect. So variables. Variables file, everyone forgets what variables are, especially when you've just got a plain old text editor. So if I was to make a new file ... And we'll call it import files, so I'll add my little Flow declaration at the top to make Flow pay attention to it, let's just call it vase to save my spelling. So I can go and say that module probably exists 'cause if I'd have misspelt it, it've moaned at me, so that's kind of nice. But now I can kind of start accessing those variables without having to remember what was in the file which people's editors do, but I kind of really like that, that it kind of doesn't feel like it's quite gone into ID then, it's still a text editor but that's just kind of a side effect of Flow just inferring the types, and if you quickly look down here, you can see the 100% and that's the kind of coverage Flow has over that entire document, so lots of 100% is good. If I remove this, aw, sad, it's gone, so that's been really nice. Yes, amazing React support, so I've been doing tonnes of React and React Native, primarily all in React Native, and ... So some of the really nice things it can do, we've got, so at the top it's verifying that we've got React installed, which we do, I've kind of defined the custom type which is expecting to receive, well, the button actually is gonna receive some props, so you can see in the function that the button should expect it on click, which is a function, which should return void, which in Flow is kind of the same as null and it should also have some optional children which should return a react element so you can actually test to see if it's returning kind of JSX if you will so down here on my app, I can see this is where I'm using button, and this is what hasn't been super useful in terms of, because it's kind of telling me what it should it return and I kind of want this to be able to tell me what props should be passed to it but at least it comes up in the errors, so you can see here, it says property on click, that satisfies that, and that being able to kind of find, like, entire components and kind of explicitly say these are the props that the component should have just makes component way, way better especially because I think, you know, in a large project, I've kind of gone to, like, 70, 80 components and trying to keep track of all the different things, it's obviously a nightmare and you can't remember every single thing so being able to define it is really nice and you can do again, you can do it with the kind of ES6 style component so you're kind of extending the React component and if you remember, the ... If we go back to the classes, if you remember how this has its kind of instant properties defined, I can kind of show you very very quickly before everyone gets bored of typed code. So you might quite typically see that which comes from React and obviously a very important thing in React is state, so you can define your state much just like an instance variable, so your state can only contain, let's just say foo, which should be a boolean, so in the constructor, if I try to then go this.state, and then try to set it to a string, it's then gonna be, like hey, what are you doing, why are you trying, you know, why are you trying to set this to a string instead of a boolean, and quite often you might, forgive the spelling, you might often have this thought, set.state, and if you are setting state with a function, so the set.state function passes the current version of that component's state in, so when you're returning a new state, you can kind of say, this function should also return back my state, and again, you can get the same type checking in there, so if I set it to false, it's gonna be happy again, so having that kind of coverage on your state when you're, like, React is very much about the state, having that rely on your state and your state be kind of type checked just removes so many errors and very quickly, same again for props, so obviously, props, very important in React. So you can kind of define the kind of props, you could do it in line, so you could do the exact same thing I did up here, so I could go onto my button, put my button props in there, and if I remove my button component, I get the same kind of error checking in there as well. And that's it, that was Flow, thank you very much.