Enterprise TypeScript

App Setup & Type-Checking JavaScript

Enterprise TypeScript

Check out a free preview of the full Enterprise TypeScript course

The "App Setup & Type-Checking JavaScript" Lesson is part of the full, Enterprise TypeScript course featured in this preview video. Here's what you'd learn in this lesson:

Mike begins the TypeScript refactor by verifying TypeScript is already installed as a development dependency and then making adjustments to the TS config file to enable type checking. Errors previously tolerated with the allowJS option are fixed. JSDoc comments are used as a temporary solution for components with children.


Transcript from the "App Setup & Type-Checking JavaScript" Lesson

>> So we're gonna be operating in your workshop repo in the packages/chat folder. That's the Slack app. Let's go through the basics of how to use this thing. I'm gonna make a quick commit. Just in case my computer melts or something. I don't like holding on to code for too long.

All right, so we're in this chat folder, so you should be able to run yarn dev. And then after some build happens, may take longer than what you're seeing on my screen cuz I have some cached build stuff that was already there for me, you should eventually see serving on localhost 3000.

And if you click that, you should see a Slack app. So first, we need to get TypeScript into our tool chain. And let's just check our devDependencies, it might already be in here. It is, okay. So I took care, there is one copy of TypeScript installed in this whole workspace, it's the same thing used by all of the different packages.

So I just put this here to make sure that we got that benefit, we're using the same version everywhere. So let's go into the tsconfig for this project. The thing we're gonna add here, so note that I already have allowJS here, which allows JavaScript to be part of my program.

checkJS will say, I want to get errors, type errors in the JavaScript files if they exist. If you say allowJS, you could just import JavaScript files, you can use things from JavaScript files. But TypeScript won't look inside those files and give you feedback about what works and what doesn't work.

So I would argue TypeScript's already part of our tool chain, but it's not doing any type-checking right now. In our src folder, it's a 100% JavaScript in here, everything is JS. So let's add checkJs true, And hit Save. And we should see some errors. One way we can do this is by running, if we kill this server by hitting Ctrl + C, we can run yarn typecheck.

We could also open up some of these files and we'd see some errors. But this is a command I wrote so we could see this real easy. You might wonder, why do I have a typecheck script instead of a build script? Well, this is an example of a project where I have something called parcel doing the building for me.

So the typecheck command runs tsc directly with dash dash no emit. So it's doing all of the work that would be necessary to do the compile, but it's not putting any files in a dist folder or anything like that. Cuz I don't wanna build the whole project, there's Gatsby behind the scenes doing some work.

It takes a little while to build. It's useful to have a typecheck function like this so that you can sort of quickly swat at type errors. So we have 6 errors in 3 files, let's take a look. And I'm just looking at these to see what the names of the files are.

I want to start from the deepest files, the things that have the fewest imports, and sort of work my way up. It's possible to do it in any order, but if you do it from the top down, sometimes you end up having to loop back to the top and fix other things.

So I kind of just try to start with leaf level dependencies, just in terms of the modules. If you made a paragraph of modules and their imports, I wanna start with the things that are at the very lowest level and work my way up. So let's start with channel.

I left some hints in the notes, let's look at those before we get too deep into this. Just so we don't have to hunt around too much. First off, maybe you put this on your clipboard. This is a React functional component type that could have children, the children property, with no specific type for props yet.

So if we find things that are objecting to children, this is a very, very basic type to put in here. If you haven't taken the fundamentals or intermediate course, these are type params between, sorry, between these little angle brackets. But that's all this is, it's a React functional component.

And then here's a little pattern around useState. Do you have question about the first piece?
>> It just looks like, is a combination of JSDoc and TypeScript syntax, is that?
>> It does look like that. If you took the intermediate course, where we talked about declaration merging, where you can have a value and a type piggybacking on each other.

There are some types that are sort of arguably making their way into our JavaScript files. React has types on it, and now that TypeScript's part of our program, we can use this. Remember, we're inside a comment context here. This isn't new code that JavaScript needs to know how to run, but it's TypeScript that's looking at the types here and using them to check out our code.

So you can write a wide range of TypeScript type information in this JSDoc stuff. You may be used to seeing a string here or something like that, but you can put whatever type you want in here. If you import something that has an interface, you can use that interface in JSDoc.

So the second thing I wanna point out here is React.useState. So initially, useState, you'd begin with an undefined state if you don't put something into useState. So we may need to fix that because React, or this list and setList things, these are gonna start objecting to us using the state, us working with this.

This isn't an array, it's an undefined, so we'll need to do something there with JSDoc in order to make that work. So with that, I'm gonna put this on my clipboard, and let's get started. And here's that useState problem. So if we go up here to the top of Channel.jsx, you can see we don't get a type error here.

We got it lower down, saying, map does not exist on never. Never just means this has been narrowed to the point where TypeScript says this is an impossible value, you shouldn't even be able to get to this piece of code. And part of that is because messages starts out as undefined, which is always a falsy value.

And look at this, we have a little thing that says, if it's falsy, anything falsy, we're gonna return. And so our TypeScript's wondering like, what could you possibly find useful about referring to messages below line 19? You've determined that you should always exit if the types are right.

If it's really undefined, you should kick out of the function here. So why are you trying to reach for a length property here? There should be no messages happening at that point. We're gonna need to make a little adjustment here, and what I'm gonna do is I'm gonna create an initial value.

And you could decide what you wanna do here. Let's start with this. You could start it as null or undefined or even an empty array, but here's what we can do. type, any or null. Let's check out this thing. Hey, it's an array of anys, which could potentially be null.

Anyone have an idea of why we're not seeing the null piece over here? It's kind of odd. Let's delete the any array, let's see if it maintains the null. Well, it's good there. What if we did this? It's like 49 is a subset of numbers, so we may as well just say, it's like this value can be any number, but it can also be 49.

Well, that's included in number, we don't need to say the 49 at this point. So what's happening here is we have strictNullChecks turned off. Null is an allowed value in any type. And so it's saying, all right, you gave me an array, any array, an array containing anything.

And because null can be part of all types, that's just in any array. Null is already in there, just like 42 is already in the number, right? That's what we're seeing here. I'll leave it this way, cuz eventually, we'll turn that flag on, and then there will be a distinction here between is it an array or is it null?

We're gonna pass this into useState. And here's our messages value here. We're getting nothing yelling at us about length. We're getting nothing yelling at us about map down here. So we've addressed that piece, but I see that we're having problems with loading here. Property children is missing in, then it looks like kind of some representation of the props I'm passing in here, if you've used React before.

It's missing, which is required in this other type. So let's go into Loading, and it turns out we have children here, right? And what we can do is just add the thing that's on our clipboard, which it read saying, React.PropsWithChildren. Children, it's gonna be present on this type.

Interesting that it's not. This should be enough to make it work, but that's not really the type I passed in. Maybe PropsWithChildren defaults to any, who knows. All right, so no more errors in Channel that I can see. Let's run typecheck again, see where we're at. I'll open up this one, we haven't touched it, so this is probably up.

I bet it was the loading problem popping up here yet again. 1 error in 1 file. Okay, what do we got here? Man, in our JavaScript code, we had a really troubling error, where we tried to get a DOM element with getElementById. And then we compared it to false to see if the element's present or not, and that's not how we do things.

We gotta say, Oops, that was a double if, though. That's a much better check there. So this is an example of something that was tolerated with allowJS but it wasn't picked up until we had real typechecking here. Now, we should be able to typecheck. We can run our app after these passes and make sure that it actually works.

It's running on 3000. Refresh, there it is, all good. So we've completed step one. React is now typechecking things, it's typechecking our JavaScript. A lot of what I'm benefiting from is things like this, right? And if I hover over these, you'll recognize these as interfaces and type aliases.

They're part of the fetch API, and so I have access to them in this file. This is how you denote an optional property here, although I'm not sure it's, there it is. See, look, look at init with the question mark after it. Square brackets means optional in JSDoc.

So even if maybe your team's not ready to fully commit to using TypeScript, you can get a lot of value out of js.comments. And it lets you sort of hold your ground and get to a closer place where enabling TypeScript using TS files would be easier.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now