Production-Grade TypeScript

Production-Grade TypeScript Typing 3rd Party Libraries

Learning Paths:
Check out a free preview of the full Production-Grade TypeScript course:
The "Typing 3rd Party Libraries" 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 updates the compiler settings to not allow implicit any types. Parameter types are copied from JS Doc comments or explicitly typed as an "any". This segment also explains using the hard-privacy syntax to prevent a property from being accessible outside of the class.

Get Unlimited Access Now

Transcript from the "Typing 3rd Party Libraries" Lesson

>> Now let's begin step two of converting this app from JavaScript to TypeScript. So we mostly allow implicit entities so far, so I'm going to forbid them making any explicit. This is going to require a change in our compiler settings, it's just one line in our TSConfig. So we're gonna say noImplicitAny true, And we can run TSC.

[00:00:34] The only reason I've been running TSC instead of yarn build is there are other aspects of the build like the styles and I don't wanna worry about that so much. I want us to have a nice little quick iteration here. In fact, I think I could do --watch --preserveWatchOutput, And let's see, great.

[00:01:01] So now we should be able to see automatic refreshes as we go around and kind of play whack-a-mole here. So now there are these things where implicit entities were flying and it's our job to get rid of them. So this is more bringing things in from JSDoc typings into the function signature itself.

[00:01:31] And we'll bring that return string here since it was in that comment, Cool. So that's done App.tsx, let's see what's needed here, Match. Where's that? It's here, but I'm not seeing a squiggly. I mean, it's on line 40, so I'm pretty sure that this works. If you've used React Router before, we need to just specify that this match is where we find our URL params.

[00:02:10] So we've got /teamId, right, that's like a dynamic portion of the URL. And we just need to state it as such. So, Something like this and this type here, right, so I'm saying here's an object, I'm destructuring, right? I'm gonna get an object, it has this property on it called match or match is this thing from React Router.

[00:02:41] And for my URL, the placeholder I have is called teamId. So it's coming from here, react-router-dom. So react-router-dom it's exposing some type information to me, which is great. All right, what's going on here? So I've got this useAsyncDataEffect, I can tell you what this does. And we don't need to worry about how it works too much.

[00:03:11] But this through the use of the effect hook fires off an API call and effectively it updates a piece of state when that API response comes back. Again, I'm simply just taking things out of the JS.comments and putting them into the function signature. So there's number on and here's number two, this is like my little options object and then we return void.

[00:03:44] So options, paste, and then return void. Now promise is gonna want some type information here, right, it needs to be a promise any, which is still fine at this point, right? We're at explicit anys, interesting so this was a typo and my JSdoc. Now, this shows you that I didn't really get validation in my JS.comment, right?

[00:04:13] So this is not a valid type x, is not a valid type here, but I did not get red squiggles in my comment. Because sort of a best effort to use that type of information. So here, I'll say, I take it any and I return a void, cuz that's my setter.

[00:04:33] This would be the setter that comes with us state, if you're familiar with that react API. So down here, this is an any, I'm just gonna make it explicit, right? This is one of the bullet points in my slide for step two, explicit entities are fine, we're not trying to make this perfect or anything.

[00:04:56] So I'm just gonna catch up with my console here and just keep clicking on files and keep, basically, here I can be a little more specific. Since, I already have a pretty strong signal that this is an error, like an error object coming in. This is gonna return a string, other things in this file, this one's interesting.

[00:05:20] So, This in many cases will work, in fact, it will always work. It's not always gonna be useful, when I say it will always work, I mean sometimes you'll get square bracket object not the most interesting string. So I'm just gonna need to, I'm just gonna disable this cuz the whole point here is I'm trying to print an error message out to the console.

[00:05:55] And normally, I wouldn't wanna do it but, normally, I would wanna do something more sophisticated. But it's sort of a last result of last resort at this place. What I mean, in a little more detail is, I'm checking to see if this value, this error thing, it's an instance of an error.

[00:06:14] And if it is, I'm printing it out in a in a nicer way. I have a method, all right, so I have a function here to Stringify that Error. In the case where it's not, if it's a truthy value, that is not an error instance, the best I can really do is Stringify it, attempt to Stringify it.

[00:06:37] So we're already in last resort territory, so I feel like it's fair to just try to coerce it to a string there. All right, we have a deferred here, raise your hand if you know to deferred is? Nobody, Mark knows what a deferred is. He knows what a deferred is because he did a lot of work with J Query.

[00:07:01] This was part of a lot of early JavaScript libraries, I used it in Dojo all the time. I want you to think of a deferred as an inverted promise. Meaning I can hand you a deferred and I might listen to a promise, and then pass you the ability to resolve or reject it.

[00:07:23] So imagine if you're spinning up, if you're opening a modal and you want to give someone else the ability to close it or to do something when it's closed. So, there maybe some one side needs the ability to resolve reject, one side needs the ability to monitor for when that happens.

[00:07:46] So this is sort of an inversion of control and I need to type this stuff now. Because these are entities at the moment, actually, I wanna follow my role and we're not going to get too clever here. We're just gonna make these anys right now. Who's seen this hash tag at the beginning of the field before?

[00:08:09] Show hands, Couple of people, Landry and Peter have seen this before. So private class fields, this is that hard privacy we were talking about earlier. Meaning even if you set a breakpoint like a debugger, and you looked at one of these deferreds, you wouldn't be able to directly access these properties.

[00:08:32] You'd be able to get at these down here, cuz these are exposed to the outside world. But I wouldn't, for example, be able to grab a deferred and shove a new promise onto it. Cuz it's a private, the source of truth is that private field. All right, still playing whack-a-mole with errors, okay, we're almost at the finish line here.

[00:08:57] There's our string, there's our date, delete, delete, save, one error, what error at the last one. But where is it, we're moving so fast that our editor can't even keep up. So there's line 22 of Team.tsx, so there's match. So this hasn't picked up yet, but I believe, I bet if I restart my TS server, If I restarted out there it is see.

[00:09:29] So on a larger project, this isn't even that big of a project, but as you're refactoring this as you're changing things, sometimes you just need to kick the server to get it back in gear. Okay, so this is our React Router type and channelId is the dynamic portion of the URL this time.

[00:09:52] And let's use our auto import magic match from React Router seems good. Save Found 0 errors, so we're done with step two. Seems like a lot, but let me recap. It was a lot of code changes, but a lot of the same code changes. So in many places, we added this, we had to in order to get JSX happy with us, we had to say this is not just a function that takes an object with the team, right?

[00:10:22] This is a React function component, there are other aspects to a function component beyond the props that you receive. For example, it returns a JSX element, it takes as an argument, children. These are not things that show up in your code, but they're definitely parts of that type.

[00:10:42] And so TypeScript is not gonna be happy with you until you put that there. And the reason you don't see them in your code, they're part of what you were really writing when you're using JSX. They would be explicit parts of your code if you used react in a TS file without JSX, you would have to be passing children around.

[00:11:07] You'd have to be explicitly calling these functions as functions, this kind of brings to the surface what is JSX actually doing for you behind the scenes. It gives you a nice familiar syntax, but all these little, like, the reason we had to apply this little tweak is because of what JSX really means under the hood.

[00:11:32] The other things that we had to do were for our routing, we had to deal with React Router, right? We had to say, look this match thing, I have to thread that through, I have to state what is this match thing. And that's an important aspect of us being able to get that dynamic information from the URL.

[00:11:55] By the way, let's see how the apps doing, let's make sure it actually works. Look, I'm happy that builds, oops, just sec, I may have something that's already running. In fact, I'm quite sure I do, Where might it be? All right lesson in trying to find processes, So I use htop, filter and it looks like, I've got a lot of note here.

[00:12:44] Just go to check my other terminal real quick, yep, there it is, I'd my Parcel Bundler working in my other tab. Now, it should work, so the error I was getting is like the port is already in use, oops. All right, time for the nuclear weapon, [LAUGH] You don't wanna do that a whole lot, by the way, oops, what's going on here?

[00:13:12] No such directory App.jsx, I'm gonna chalk this up, well, let me finish my previous thought, I feel I don't wanna get too many tangents deep here. So you don't wanna do this too often, this stops things like language servers, which maintain incremental build information in the background. So if you do this all the time, you're defeating some of the development tools that are storing state.

[00:13:52] And trying to do as little work as possible in order to make your offering environment nice and responsive. So I would only do it if you find that you're sort of trying to hunt down a process doing weird things like, it's a rare and extreme thing. All right, now, let me talk to you about this error message.

[00:14:09] So it's App.jsx, obviously, my file is no longer App.jsx it's a App.tsx. I suspect that there is some cached information at some level, from the old version of this app that is still hanging around and causing some problems. I'm guessing it for two reasons, one, I don't have a file by this name anymore, I used to, I don't anymore, so it's old information.

[00:14:39] Number two, I just stopped a development build that was happening in another terminal that all while we were editing this thing, it's been doing its thing in the background. So it may have gone roke, this is where it's time to blow away your cache, that's the first thing I would do.

[00:15:00] So let's see if we can find where our cache is, all right, so ls -a, so that you see all these .files, here's a candidate. There's cache .cache, let's try that and dist, And then let's try building again. Already things looking better, There we go, another thing you don't have to learn the hard way.

[00:15:45] Okay, let's check it out, seems like slack, seems like the App works at a high level. And have a test suite too, but it's also good to just make sure the App is actually an App and it fires up. So that's great, I think it's time for a second commit.

[00:16:08] So we're saying Convert to TS, and then we're gonna call this step, no implicit any, and push.