Check out a free preview of the full Making TypeScript Stick course:
The "Penpal Types Solution" Lesson is part of the full, Making TypeScript Stick course featured in this preview video. Here's what you'd learn in this lesson:

Mike walks through the solution to the penpal types exercise.

Get Unlimited Access Now

Transcript from the "Penpal Types Solution" Lesson

>> Just a reminder a task is to produce a type that's similar to this here, the type of this methods object. But we want to see every non promise value that is returned from a function wrapped in a promise. So first what we can do is check to see if this object has a lot of functions on it because effectively we need to kinda like extract out that return value and maybe wrap it in a promise somehow.

[00:00:32] So we'll probably need a map type, right? Anytime you iterate over the keys of something, and you're producing new values or new value types, map type is probably what you want to do. So here we're gonna say K in key of T, good. Great and that compiles. So here, then we're gonna say, all right, so if the value found that's associated with a different key is an instance of a function.

[00:01:06] Sorry instance of it's not the word we use here extends function. So this is gonna be our call signature. And we're gonna use infer to kind of pluck out that return type. So we're beginning a conditional type here and we're saying look, if it extend this callable thing, by the way while we're performing this check go pluck off that return type.

[00:01:39] And then we could say promise are otherwise never and let's see what we have so far. So what does async methods look like? We've got add and subtract, sorry, those are now promises that resolve to numbers we need them to be methods that return these promise. So we can go up here so effectively we can just alter this branch.

[00:02:18] So we'll say, Infer args, we'll use our variadic tupple type there. That's easy we kind of capture the args capture the return value. And turns out that this looks like the solution here at least a basic solution, right? So, important things to look at here we're using a conditional type, here's the extends keyword.

[00:02:52] The branch if that pans out is true, this is the false branch. And then we can use this infer keyword to sort of, as we're matching against this pattern much like a regular expression right where we can say. Do you match this pattern? And by the way, while you're making the match, pluck out this part of the email address, give me the domain, give me the user ID.

[00:03:13] And that gives us an opportunity to sort of refer to the arguments and the return value in a different way. So let me add an async thing here and see how it works. Right, a function that already returns to promise. That resolves to a number, And let's see what happens here.

[00:03:41] So I would hope, Yeah, so this is not desirable see how we're returning a promise that resolves to a promise so that resolves to a number. There is no need to direct promises like this. We chain them, we don't nest them like this. So how might we address this?

[00:04:08] Well, an easy way to do that is to say, in this branch if our extends promise, any then do that. Otherwise, our return value is just, R right. Sorry I might have my condition reversed here if R is already a promise. That's where we want to just send it right through.

[00:04:41] And then this is if it's not a promise, then we we wrap it in that promise, and we need this. So, All right, and let's see what we've got. Do a sync thing perfect. It's a promise that resolves to a number. Add is a promise that resolves to a number.

[00:05:06] So we're just taking the things that kind of match we're iterating over all of the keys. And if the value is a method, anything callable, then we're doing this promise wrapping and only where necessary. So if we have as an added benefit something like this, That's never why we end up with an error because it's down here we can make we could let that pass straight through just for fun as a little extra thing So here you can see foo could either pass through as number we could have it passed through as a promise that resolves to a number this is kind of where we would handle none method things, right.

[00:06:02] There are no tests in either case that deal with this. So it ends up being quite a complicated type, but the key here is to break it down into small pieces, sort of sanity, check yourself as you go along the way. By the way, all the tests that we've written we've coded against for type information today are using this great library called TSD.

[00:06:21] That's where this expect type thing from. This is really great writing tests for type information vision is important. And it helps especially as new compiler versions come along things may occasionally break, this helps you kind of make sure that you can tune things up without breaking other things.