Check out a free preview of the full Production-Grade TypeScript course:
The "Typing a Project to strict" Lesson is part of the full, Production-Grade TypeScript course featured in this preview video. Here's what you'd learn in this lesson:

Mike begins converting the project to TypeScript by renaming the old files to the appropriate extension. When files are renamed in a repository, the code should be committed before additional changes are made so the history is preserved. Disable strict mode and allow "implicit any" types to be used before debugging errors.

Get Unlimited Access Now

Transcript from the "Typing a Project to strict" Lesson

[00:00:00]
>> Let's dig in. So, our task here is we want to convert this library, that's gonna be our bigger effort here. Now, I received some feedback from people that tried to apply this technique to a bigger app and they just they told me that, step one was easy.

[00:00:27] Step two was relatively easy. Step three was like eight plus percent of the work and it didn't provide this idea of, I just wanna make some progress, get some ground get it merged in, right? So step three took a long time weeks in some cases which is not good.

[00:00:47] So I have broken this up further and this is what's needed at the professional TypeScript level. And here's my correction about these these strict mode things. So, step one, I would say, enable strict mode like 3.1 is enabled strict mode 3.2 this even more strict stuff and then 3.3 is where you start to apply some of this additional PS specific linting that requires type checking.

[00:01:25] This is gonna detect even more things and then this is actually typo here but we should be turning these on, right? So this will catch a lot of things even native browser API's. That returning any. So we'll want to make those in unknown instead of in any this is where we're getting.

[00:01:53] We're approaching like a level of sadness. That's it's at the upper bound of what TypeScript supports and to go further in that direction. You'd find yourself sort of getting close to Haskell. Where. And there are other programming languages like this like rust, for example, where you start to feel like basically if it compiles, it's very likely to work.

[00:02:17] Like the compiler is just it's pushing so many errors into the compile phase and it's able to detect so much. Usually this these compilation processes are very slow, because there's a lot more work. But we're starting to not approach that level, but we're as close as TypeScript will allow us to get when we get to 3.4.

[00:02:40] So our challenge here is we're gonna follow steps one and two and convert this workshop app. And I'm gonna do this we're not gonna break and then I'll work on this individually. I'm gonna do it with you here but that's what we're setting out to do. I've given you because renaming is a lot of the work here, given you some copy paste commands that will change your JS files, JSTX files to sorry JSX files to TSX, etc.

[00:03:11] So if you invoke all of these in order you should end up with everything being the TypeScript version of these files. And then like this is a big part of the mundane annoying work. So you don't have to get in rename each file individually. So I will be running each of those commands in sequence and we'll see what we get.

[00:03:40] So this one is everything in my source folder. Going from JSX to TSX, let's see if that worked. Looks good, that's a TSX file in our four renamed seems about right and then anything that's left over that's just a regular JSX file like that index JSX here. That should take care of that and it does and then the tests start as JSX tests, another TSX.

[00:04:26] Looks like my comments in the file are bad but the commands are good and then this last one. So if we look at our GIT or staged, GIT stuff see all these RS for rename. We've just renamed these files and I wanna make a commit right now and here's why.

[00:04:53] GIT is pretty particular about what a rename means. And if I were to start modifying these files too much more before making a commit. It will start to view this as the deletion of the old file and creation of an entirely new file. What's wrong with that? I lose my history.

[00:05:19] I don't get to track this TypeScript file back to when it used to be a JavaScript in a single history. I like having that on a single file, it's good to preserve. So if I make a commit now, even though some things here might not work then it'll be a rename followed by an edit.

[00:05:38] As opposed to the deletion of the old file and a creation of a new, completely unrelated file. I don't want that because that's like one history ending and then the other beginning that's bad. And I wanna make, I'm gonna make a branch called FEM, for Front End Masters, and I'll be making all my commits to these and I'll push them straight up.

[00:06:05] This one will not work, because there's other things that must be done to this file but the commits important. Oops. Rename JSTS, git push, origin not origin master. Nope, not today. [LAUGH] Okay, now we have to get things compiling again. So I'm gonna run tsc and let's see where things are bad should have a bunch of compile errors.

[00:06:52] Interesting my scratch .ts is a problem, I'm gonna just comment this out because this was sort of just notes. Okay, 64 errors. So. This is we've forgot to do one thing here, which is important, and that is, telling the compiler to back off. So I'm just following the readme instructions here.

[00:07:23] So we've run our little scripts, and then I'm saying I have a note that says don't forget to make a small change to the index.HTML, which is the entry point. That the bundler here is looking at. So we were loading index js and I want to load index ts now make that small little change.

[00:07:44] That's all put that index HTML away, you'll never need to touch it again. And then I wanna comment out this "strict" true and I want to enable. ImplicitAnys, that's a double negative there. "noImplicitAny", the thing that forbids ImplicitAny, disable it. I hate double negatives but no implicitAny, turn it off.

[00:08:22] So here we go strict true coming out and then no implicitAny. False, all right, so we had 64 errors, let's see how many we have now. Ten, lot better already, okay, so let's start checking some of these out. I'm just gonna open all of the files that that these errors relate to, I can just come in and click on them.

[00:09:13] Since there are only 10 of them, okay so. Here let's take a look we have a missing return type but that's just a warning. And implicitly has an any type because the expression of type Edie can't be used to index this. All right, so here we've got an empty interface, so we can fix this by doing string.

[00:09:44] So now it's just using a string to index this and it's still unhappy because this thing, this cache channel records, the intent here is for this to be a dictionary. So we should type it as such, we can say this is a record. And this is a utility type that comes with TypeScript.

[00:10:05] It's almost like there's a little standard library of types, and so this lets you specify an object whose keys are of some type in this case strings. And values are of another type, and is. And now, so now we got rid of a lot of the errors here.

[00:10:28] What is this? So we're saying argument for a git was not provided. Okay, so look, let's learn more about this, I'm gonna just jump into this API call thing. All right, so I take two arguments here. This square bracket thing. Maybe it's this, yeah I think this this might have been a little js doc mistake on my part, but we would, still have had a similar problem.

[00:10:56] These are ways of indicating that, you know, params might be optional. TypeScript has a different way of doing this, so let's just move this type information out of the comment. And into the function signature itself, so we're just kinda converting these. All right, and let's go back to think we were here we were in channels and look okay, now we're okay.

[00:11:27] Right that second argument is optional, so channels looking at my gutter. I don't see any red, so I'm okay with warnings we're, not trying, like a major risk here is going too far. Letting the perfect be the enemy of the good. You want to get in make some progress, get that merged and you don't wanna try to bite off extra pieces.

[00:11:54] Beyond this, you can always make another poll request, but what the longer you drift away from trunk from the sorry, the longer you drift away from your default branch. Like main or master, the more trouble it's gonna be to get this merged in. And all the while, potentially the rest of the team contributing to the project.

[00:12:15] They're still writing more JavaScript, they don't get the benefit of you having converted this. So you wanna make progress and then land it. Same change here that we made to the the API method, making sure that in it remains optional. Get rid of these two, and we don't need this anymore.

[00:12:40] All right, we're down to 17 problems, it see this includes warnings to it wasn't 34 but a large portion of these are not errors. So here we've got get channel messages, I know both of these are strings. You would be able to see that they're strings If you looked in that DB.JSON file and you're in the root of your project right there, just string ideas.

[00:13:07] Here we need another record where the key is a string, the value is in any. And now we're back down to warnings, right? Making great progress. This let's see, we have a cached all team's list. And I believe this is a promise. That resolves to an array. And that was enough to fix this, because API call returns a promise any.

[00:13:43] And in this case, I know I'm getting an array of records back that's why. But we can already see that it's a little wishy washy here, right? Promise any and now I'm saying yeah store that in my promise array of entities. Well, we this will start to be something we'll have to deal with as we crank up our strictness.

[00:14:04] And then, here we need a record of string to any. And string, and looking down here, all I see are warnings. So I'm going to try to compile once more. Still got errors, maybe I forgot to open one of these files up. Yep, so that wasn't show VS code is only gonna show me problems in files I have open.

[00:14:41] Okay, so here, the problem that I have is I have a couple problems. So status here is a number. In a return an HTTPE error, kind right, I have it right here, so I can get rid of this. Now, for some reason, this isn't being recognized as a type.

[00:15:10] Well, here's why, I have it sort of as close to an a new as I can get. It's really should be in the new right but it's though it can be used kind of like in a new. Let's make it a real one. Delete this, change every colon, to an equal sign, save.

[00:15:43] Now I can return a value of that type, right? So it's a pretty, very just a mechanical transformation, It's not too complicated. And then here, I just need to pull things down from my js doc comments and make them real types. And then when they're left, kind, right, I have to state that this class field exists now coming up in TypeScript 4.1 the data as of this moment.

[00:16:19] I would be able to just do kind, and it would be fine. Who actually looks like this may have already landed. So until recently, I didn't even expect this to work to be honest. Until recently, I would have had to specify this type, but because I'm initializing it here we can kinda figure out what that type would be.

[00:16:42] So it removes that extra level of needing to specify things in two or three places. Let's check to see if compile works now, surely we are close. It's taken a while, It's a good sign five errors. Okay, still chasing some things down.