"Automatically Migrating to TypeScript with ts-migrate" - Evan Shaw (nz.js(con); 2021)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
um our first speaker this afternoon is evan shaw and i will grant him the honor of introducing himself everyone give them a round of applause [Applause] all right so actually rather than introducing myself immediately i want to start with a story and so at my job we've got this large front end application it's mission critical it's the most used part of the product it's complex it's got a lot of logic it has to work offline and we are making changes to it all the time so we want to make sure that we can make these changes safely and it felt like a natural choice to introduce typescript and so about three years ago i committed the first typescript to the repository um you may not be able to see it but there's a tiny dot in the corner in the bottom left and the idea was that over time we would convert file by file and eventually the whole code base would be typescript and so some time passed and about two years later i checked in to see what sort of progress we were making and we had migrated 40 of the code base so not zero progress but also not the type of progress that we'd like to see because at this rate it would have taken five years to migrate all the way and that is just way too long even two years is a really long time for this type of migration but the problem was we couldn't just stop everything we were doing to finish the migration we we really couldn't even slow down that much because there was just a lot of other stuff vying for our priorities but luckily one of my colleagues came across this open source project out of airbnb called ts migrate and it was a tool that would automatically migrate a code base to typescript so i gave it a spin and it showed some promise and i made a few pull requests and then the next week we had converted our entire code base to type type script no errors stricted strictest settings no bugs when we deployed it and now we can convert a code base in an afternoon so i'm evan i'm a principal engineer at vend by lightspeed we are hiring with remote possibilities in new zealand so go have a look at our website if that interests you and i'm also a ts migrate contributor and today we're going to talk about migrating to typescript what are some of the challenges what are some of the approaches you can take then we're going to talk about ts migrate and how it works and how you prepare for migrating code base and finally we're going to do a live demo so everyone cross your fingers for that one so first migrating to typescript why typescript this is not a typescript talk this is a migrating two typescript talk so i'll try to keep it brief but basically we need all the help we can get in a project especially as it gets bigger or older or has more developers working on it there are many tools and processes you can introduce to try to prevent mistakes i think typescript is a great one personally but i'm not going to spend a lot of time trying to convince you in this talk second i want to talk about the any type so you may if you're passingly familiar with typescript know that you can apply types to variables and function parameters like string number object types and the most general type that you can apply is the any type it's arguably the most useless type you can apply like if if you give a variable type any you can basically do any anything with it and so there are no constraints and it's not very useful but it is quite useful as you're migrating to typescript because sometimes you just need to chuck in any type onto a variable and leave it for another time and there are basically three approaches that i can see that you you might take to migrate to typescript the first being the big bang approach so this is where we're going to take every file on the repo we're going to change the file extension from js to ts and then we're going to see thousands of typescript errors and we're just going to fix them all right as you can imagine this has some pros and cons so a probing it gets it all over with you're done you don't have to think about it anymore you get all the benefits from typescript immediately after you're done but there are some downsides it's hard to do other things concurrently like either you have to tell people hey stop doing the other work you're doing while i convert everything to typescript or you have to continually merge in the changes that other people are making which is a big pain and it can be tempting to disable strict settings so typescript has a number of stricter options that you can enable and i suggest that anybody who's using typescript do so but if you're faced with the possibility of you know 3000 errors if you use the strictest setting and settings and 1000 errors if you use the least strict then those 1000 errors start to look pretty tempting but the problem is there's no real path to getting to the strict settings without fixing the other 2000 so another approach you can look at is the gradual approach and this is more like what i described earlier where we're just converting a file at a time and this also has some pros and cons the one pro being you don't have to do it all at once i think this is a really important property of any big migration that you're doing is that you can do it bit by bit and you don't have to stop all the work that anyone else is doing or continually merge things in and maintain your work um but as we saw in my story earlier it takes a long time especially if you're having trouble prioritizing that work over other work and also a file can still be a lot of work to convert by itself sometimes files can be thousands of lines long we had some cases like that too and so it can be very hard to say oh i'm touching this file i'm going to convert the entire thing to typescript and so then we have the third pro approach the automation approach and this is what i'm going to focus on today with the tool ts migrate this is the fastest approach because it is basically all automated it can address all errors even with the strictest settings but one downside is it does give you worse types so you can imagine that if you have a human applying types by hand you're probably going to get better types than if you have an automation script that does everything automatically and so it is a downside but i think it's a downside that is worth it considering the other upsides and you can still improve things over time and you don't have to improve the entire file at once you can just improve a single annotation at a time so it becomes quite easy to just say oh i'm touching this area and up here there's this annotation that could be a bit better so i'm going to change that too so now we're going to talk about ts migrate and how it works what is ts migrate it's an open source project out of airbnb so basically they had a bunch of javascript and they decided they wanted everything to be typescript so they came up with this project because it would have been just too much work and take too long to do everything by hand it transforms source code using what you might have heard called code mods so it parses each file transforms the the parse representation and then spits it back out again and it's plugin based so that each plugin can focus on a particular type of typescript error and um just be responsible for certain things and you can also write your own plugins um it's it's a little bit hard to run your own plugins at the moment um but that's something i'd like to improve going forward so at this point you might be thinking hey this sounds like magic i'm just going to run my code through this magical tool and it's going to transform everything and give it perfect annotations and it's just going to be great and if that's the expectation you're going in with you're probably going to be pretty disappointed because there's no magic right you should basically go in with the expectation that it's going to apply the any type wherever it possibly can maybe do some ts ignores and if you go in with those really low expectations you might be pleasantly surprised by a few things that it is able to do and there's also this convention that you can apply to your code where you declare this alias called ts fixme and it acts exactly the same as any but the reason we might want to use this you could be thinking oh this looks really ugly why would i want this in my code well that's kind of the point so you set up this alias for any and then ts migrate is going to use this alias anytime it has to use the any type so you know this is something i should fix later it's basically like a to-do in the code and i'm going to go through a few of the plugins and kind of what they do to your source code so the first one is declare missing class properties so here we've got a square class and it has an area method and that method is referencing a w property and an h property which presumably are the width and the height of the square and if you run this through typescript typescript is going to complain hey you're referencing this w property that doesn't exist anywhere same thing with the h so the declare missing class properties plugin is going to come in here and it's going to add declarations for any property that is used within the class and it doesn't know what type the property has so it just uses ts fix me you could imagine if it were a little bit smarter maybe it could figure out that these are numbers but this is as smart as it is at the moment another important one is add conversions so here we've got a variable where we're setting it to an empty object and then later on we want to set the a property of that object and typescript says hey property a doesn't exist on empty object so ts migrate will add a conversion here to ts fix me again and hopefully some at some point later you can come and fix this so that the conversion is not needed anymore explicit any so a parameter to a function that doesn't have an annotation implicitly has the any type and typescript and if you have the strictest settings the typescript compiler doesn't allow that it complains that you must have an annotation on this parameter so that's what's happening here because we don't have an annotation on x and explicit any is going to basically do what the name says and explicitly add an any type here ts fix me again we we should come back and fix that later but it'll get rid of the error for now before i introduce this next plugin i want to talk quickly about ts ignore so this is a comment that you can put in a typescript file and it will cause the compiler to ignore any errors that are on the next line of the file and then there's another type of comment that is ts expect error which is a little bit different from ts ignore this says to the compiler expect there to be a type error on the next line of the file and so if there is actually not a type error on the next line of the file then this comment will become an error and that's quite useful because you know that you never have useless ignore comments in a file and so this next plugin is is basically the the last thing that runs in ts migrate it's kind of the nuclear option where if there's some other error that typescript complains about and ts migrate doesn't know how to fix it in any other way it's just going to add a ts expect error comment so here we've got a silly hello function that takes a name param parameter and if the name parameter is truthy it will say hello name and otherwise hello world so the compiler complains because we're calling hello with no parameters and this parameter is not marked as optional by typescript so typescript says hey we expect one argument here so ideally the fix is probably to add a question mark to make that parameter optional but ts migrate can't say that for sure so instead it adds this ts expect error which we can probably easily fix later by just going and adding a question mark and finally once all the code mods have been applied it runs through eslint and auto fixes all the things that can this is really useful to try to preserve this style in your code and i'll talk a bit more soon about how you can configure um to to preserve your style better hopefully and there are some other plugins there's a js doc one if you have lots of js doc annotations those can be converted to typescript um member accessibility can so it can make your properties and methods private protected or public based on naming conventions there are a bunch of react plugins because airbnb uses react um it would be nice to see other frameworks on here eventually but um this is what there is for now okay so let's talk about the preparation steps that you can take and most of the things i'm going to talk about are optional so you don't have to do them but you'll probably get better results if you do do them although the first one i'm going to talk about is actually not optional and that is prepare your build system so there are as many javascript build system setups as there are stars in the sky so i can't possibly tell you how to set up your build system but um if you're using webpack there's a loader for that if you're using roll up there's a plug-in there's a babel preset there's probably something for whatever it is that you're using and um i don't know just go do some googling i guess to try to find the best way uh es modules i would suggest that you use es modules if you're currently on um common js so if you're using require or module.export it's probably about time to switch over to es modules because that's the direction that everything's going typescript will work with common js but it won't work as well in my experience so i suggest making this change install types packages so typescript relies on type declarations to be present for your dependencies so it can know what types your dependencies use but not every dependency comes with type declarations so sometimes you need to install these third-party type packages which are usually under the types scope in npm and it would be really nice if ts migrate would do this automatically as part of the migration but it's not quite there yet so for now you have to look through your dependencies and figure it out yourself uh eslint so probably a lot of you are using eslint already um and to prepare for typescript you will want to switch to the typescript eslint parser so it understand understands typescript syntax i'd also suggest looking at the eslint plug-in prettier so it will run prettier over over your code this will make it so that the eslint fix plug-in will basically run prettier over all the files that it touches and and so that will keep the changes to a minimum if you were using prettier already of course if you're if you're not using prettier already this may be a bit of a larger decision to make but um you can think about that and so now we're going to look at a live demo and before i get into this too far i do want to call out a few places in which i'm cheating so first i'm using an unmerged branch of ts migrate to better preserve blank lines um the the currently published version of ts migrate has a problem where it will remove all blank lines in your code which made some people unhappy and i've got a pull request in to fix that but it hasn't been merged quite yet so i'm being honest about it at least also dependencies are already installed because i didn't want to risk having wi-fi issues while i was up here and i didn't want you to have to sit here while i install npm packages so aside from that this is all real yes cross your fingers um all right how's how's my my font size can you read that okay or a bit bigger is that good okay so the the library that i'm looking at here is called react date picker i i admit maybe one more way i cheated is i slightly cherry picked this example um so it's one of i did look through the most popular react component libraries i chose react because i wanted to showcase some of the react plug-ins that ts migrate has and what they can do um i also wanted something that already used prettier so that i wasn't completely mangling the formatting of the code um but i i don't think i had to try too hard to find a project that was like that so the first thing that we're going to do here is we're going to update the eslint configuration so we need to set or we need to add the prettier plug-in so so i've installed that dependency already but i haven't actually configured it we need to change the parser here to the typescript eslint parser and we're also going to enable the prettier rule so that that gets applied actually i'm going to save without formatting so i don't change everything and i'm also going to update the babel configuration now this is a little bit interesting this project actually used flow in in one file already flow being an alternative to typescript and it it actually just works although it's not guaranteed to just work in every case it it happens to just work in this case because the syntax is quite similar so we're going to go ahead and commit that and the next step we're going to go through is let's see oh right i need to add the the type aliases so i'm i'm going to set up the ts fix me alias uh ts migrate does not add that for you automatically so ts fix me any and there's another one i didn't talk about which is ts fix me function which is for something that we know is a function but we don't know anything else about it so it could take any number of arguments they could be of any type might return anything and then we'll go ahead and commit that i do suggest committing often because you might always want to roll back to an earlier version now we can actually start the migration so the first step is going to be to run ts migrate and there's an init command and all that's going to do is it's going to create a ts config.json file with some defaults and we oops i didn't want to print we will mostly use those defaults with a few exceptions so we're going to enable jsx react because this is a react project i'm going to go ahead and set no emit to true so we're not actually emitting javascript just just for the demo and the last thing i'm going to do is i'm only going to include the source direct the source directory and not the tests because honestly i see most of the value in migrating the source and a bit less value in migrating the tests maybe we could come back another time and migrate the tests later and you can use ts migrate on projects that are partially typescript already so that that's totally fine that works so i'll go ahead and commit that oh add that commit it and then i'm going to run the next step which is to rename ts migrate rename which is going to rename all my js files to ts and jsx to tsx so i should see tsx files and the jsx ones are deleted and if i add the source directory they're all just renames so nothing else is changing all right so now if we run the typescript compiler we should see a whole bunch of errors right so almost 1400 errors now we're going to run the migrate command and i'm going to pass the aliases ts fix me flag and that's what tells ts migrate to use the the ts fix me alias otherwise it will just use any and so you won't be able to distinguish between things which should actually be any and things that are just temporarily any so i do suggest that you use this flag sorry i should have hit enter before i talked because this will take about 30 seconds yeah it's just printing out the names of the plugins it's running and how long they're taking explicit any ad conversions it actually goes through two rounds of eslint fixing because it can mess up the formatting when it runs the ts ignore plug-in right so we've changed a whole bunch of files and if we run the typescript compiler we should get no errors no errors so what do we actually change we can come in and look at some of these files to to look at the diff so this is a react component um one of the plugins fired here the the prop types plugin it removed the prop types import and instead it added the prop types as typescript types so we have have all the props here it didn't know what the state of the calendar was that's okay it it understands react components so it can add the proper type for the props in the state it's removed the original prop types here you can actually configure it so it keeps the original prop types if you want added the annotations to the lifecycle methods because it has a plug-in that understands the lifecycle methods and what types they should have here we've had to add some ts expect errors right so that should give you a rough idea if we look through the whole thing we've actually got 116 ts expect errors which might sound like a lot but we can fix some of those in fairly big groups so i'll go ahead and commit and show you fixing one of those errors just to give you an idea so if we look in i think the date utils file has some good examples yeah here so here we're calling this is valid function with one parameter and if we go and look at the definition this function takes two parameters and then if we look at what the code is actually doing we can see oh the second min date parameter is probably supposed to be optional so it's exactly the case that i showed you earlier in the slide basically so we could go ahead and add a question mark here and now we will have some type errors because those ts expect arrow lines are no longer useful we've found six unnecessary ts expectora lines and ts migrate actually has a command that will let you take care of lots of these at once so it has a re-ignore command which will strip off all of the ts expect error comments and then only add back the ones that are necessary so if we run typescript compiler should have no complaints and if we look at what's changed in the diff we've removed some error lines go ahead and add that commit it so we've we've just fixed six errors and you can kind of just work your way through it i mean if we wanted to after that initial migrate we we could reasonably i think make a pull request and and call it good for the day and come back to it later and fix the rest later um and so that should basically give you an idea of the workflow um i would suggest prioritizing getting rid of all the ts expect error comments at least because those are a little bit dangerous because any error in that line will end up getting ignored um and then the the ts fixmes you can just work through as you touch things right so these were the commands that i ran i'm not including the reignor command i don't know if these slides will get shared somewhere afterwards so you can write these down but and so just to give you an idea of where things are headed these are kind of the road map items priority items as i see them so the first thing that i'd like to do is add the type packages installation automatically so you don't have to do that beforehand um have some sort of configuration file format so that you can write your own custom plugins and configure how they're used better documentation because the documentation right now is not that good and then further down the road it would be nice to try some sort of type inference plug-in i've played with this a little bit and i think there's some promise here and then finally more framework or library specific plugins you know for other frameworks common libraries that's something i'd really like to see and if any of these things sound interesting to you and you think you might like to work on them get in touch i would be more than happy to mentor anybody in contributing to the code base point you in the right direction and that's it you
Info
Channel: JavaScript NZ
Views: 772
Rating: undefined out of 5
Keywords: EvanShaw, nzjs, nzjscon_2021
Id: y7WUsi6NeH8
Channel Id: undefined
Length: 29min 13sec (1753 seconds)
Published: Mon Jun 21 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.