External Libraries 9 exercises
explainer

Navigating Express's Type Definitions

Express is a library that lets you build a REST API really simply.

Express has types that come from the Definitely Typed repo, which also contains types for many libraries that aren't built in TypeScript

Loading explainer

Transcript

0:00 In the exercise after this, we're going to be diving into the types for express. Express is a library that lets you build a REST API really simply. What you can do is you can import express from express. Express is one of these libraries that has types, which come from Definitely Typed. 0:19 Definitely Typed is a repository that contains type definitions for lots and lots of libraries, which aren't built in TypeScript. Express itself isn't built in TypeScript. If we look at the type definitions there, we'll see that, you can see there in node_modules/@types/express and then index.d.ts.

0:44 Express has some seriously hardcore types. We're going to look at a few key parts of them to understand the naming conventions, understand the terms they use before we dive in. We can create a basic get endpoint here. This app is we call express and we get back an instance of express. It's like saying new express.

1:09 We could say app.post, for instance, user, and then this would have a req, res in it. We would be able to read from req.body, for instance. If you did a post request and you put some stuff in the body, then this would be present here. There are some really crucial parts to understanding this API and understanding how we can make parts of it generic, too.

1:32 For instance, this request, if we take a look at it, it's a request with a bunch of different generics in. This app.get is like, "Whoa, OK," there's a lot going on here already. We've got a path user, which if you can see is inferred as its literal there, which is really cool.

1:52 Then we have a bunch of handlers. These handlers is an array of request handlers. We've got already request, RequestHandler. We've also got response in here, too. These responses also take generics. You'll notice that this response here, it has response any, record string, any, number. We can see that each of them are being filled in with different default generics.

2:18 Let's take a look. Let's take a look and command-click on app.get. We have an interface here called IRouterMatcher. What this does, as you can see, this is like, "Wow, there's so much going on here." This is the app.get is what's called an IRouterMatcher. It's got a method, which is one of these and it's equals any. This is really crazy stuff.

2:44 We can see then that we have route extends string, RouteParameters, ResBody. There's lots and lots of generic functions, which are being expressed in an interface. This is a way that you can declare function overloads, which is slightly different from normal, which is you can declare them inside an interface.

3:03 If I show you this, we can say interface MyFunc, and we can say this and then say this returns a string. Or if we call it with, I don't know, id number, then it returns a number. This is something that you see when you're doing third-party typing, so when you're trying to type a third-party library. It's not something you usually do when you're writing TypeScript and your files are TypeScript.

3:31 This gives you a way to say like const func is MyFunc, then it's either going to have id number or not. It's pretty hard to type this stuff, but this is the idea. It gives you a way to say, "OK, this is how I express a function with an interface."

3:50 To go back, app.get, it's one of these enormous IRouterMatcher. We've got path route, and the handlers are an array of request handlers. RequestHandler is a slightly easier to read version of this, but it's still an interface with a function or interface that expresses a function. It's got request, response, and next. You can see that these generics are how it structures like making things type-safe.

4:19 What I can do, which is really fun, is I can extract out one of these. I can say, let me zoom in a little bit, const getUser is going to be a RequestHandler. That RequestHandler is going to have a req, res. I'm also going to import RequestHandler from the top here. My auto-import is not working, so I'll bring it from up here. We get RequestHandler from express. This means you can build your own here.

4:53 Let's take a look at each of the generics here in turn. We've got P equals core.ParamsDictionary. How would I investigate that? I can command-click on it, and I can see what core.ParamsDictionary is supposed to be. It's supposed to be, "OK, this is another example of fancy aliases. We've just got a record, which is strings and strings." That makes sense.

5:16 Where does that P end up going? Extends core.RequestHandler. Let's take a look at core.RequestHandler. We're back here again. This P goes in to the request. Let's see where this goes. This is an interface, which extends http.IncomingMessage. This is like a node built in, module http.

5:38 This P, where does it go? Let's go for find references. It goes directly into params. Params is an attribute on the request. What this means, having dug through all of that, we can pass it a, let's say, id string, for instance. Now, request will have req.params.id. That's type-safe.

6:11 If I pass something else to it, let's see what else we can do. We have Request, ResBody. Where does ResBody go? This one goes into RequestHandler. ResBody gets passed into Request and Response. Let's see what it does inside Request. Let's see where ResBody goes. Go to references. It looks like it's just being pumped into Response there.

6:40 Let's take a look. Go to references again on ResBody. We've got a few here. We can send ResBody, this. Interesting. Json send ResBody, this. Jsonp send ResBody, this. This means then, if we look at the send function, took me a second to find this, now we have another alias, which is Send ResBody, this. ResBody is, by default, typed as any, and you can pass in the ResBody.

7:13 This means then that if we type that, if we say that you have to return an object with, I don't know, name string, say for instance, we're getting this user by id and we're returning the name. This means that I can say res.send, and then I have to pass in name, and let's say I just get this wrong, this is now type-safe, too.

7:37 If I don't pass this generic, it means that I don't get type safety on my res.send. It's a way of telling express what you wanted to return from this res.send. I'll tell you what the rest of them are without showing you. ReqBody allows you to strongly type the request body that's coming in. Imagine this was a GraphQL query or something where we get the id in via post.

8:04 I'm going to add any as my default here. This means that req.params is typed as any. I'm going to say, let's say, req.body is now typed as id string. The next one is the query parameters. This one is going to be...imagine that you pass id string there. Let's give this any instead. We'll say query.id. This is going to be like if you pass, for instance, user and then id equals whatever.

8:41 We then have the main three ideas here, which is we have the main three terms that we're using. RequestHandler, which then feeds into a request and a response. Each of these generics is passed through. I'm not sure what Locals is. I'm fascinated by that. This idea of the request and the response is going to be really crucial for the next exercise.