Use Narrowing to Handle unknown Errors
The unknown
type implies that the value contained in the error
could be anything.
However, it is distinct from the any
type.
While any
turns off type checking on anything it's assigned to, unknown
maintains type checking. This means we can narrow the value down to the type we anticipate.
Transcript
00:00 Okay, unknown is a type that sometimes comes up and often comes up when we're dealing with try-catches. We say catch error here, and this error is of type unknown. Unknown basically means it could be anything, but it's different from any.
00:14 We can actually say catch error any here, but any basically turns off type checking on anything that it's assigned to. So error here, because it's unknown, unknown doesn't turn off type checking. And so with unknown, we can actually narrow it down into the type that we want.
00:33 Now, there is a way of doing this where we basically know that something dangerous is going to throw an error here, and it's going to be a certain error. But let's say that we accidentally say math.random here, and we have a tsignore that's kind of suppressing the check here. So we have math.random.
00:52 Well, we might end up not getting an error here. This might actually be a type error instead. So we don't definitively know what type of error this catch is going to be, so TypeScript can't infer it. And so it ends up being unknown. So in every try-catch we'll use, you will have an error here which is unknown.
01:09 So we could use an as here. We could say error as error.message. We could make an assertion. We could tell TypeScript, no, you're wrong to think of this as unknown. This error basically is an error here. And so we error as error.message.
01:27 And so error has a few different possible properties here. Cause, message, name, stack. And we just console log the message here. We can't do error colon error here because basically unknown can't be assigned to error.
01:43 And this check here, basically this kind of annotation that we're doing here is type safe. We're not actually like yelling at TypeScript. We're not knocking TypeScript over the head. Whereas the as down here, we are actually suppressing TypeScript a little bit. We're forcing it to say, okay, this error is an error here.
02:03 So this is an unsafe way of doing it. What might a safe way of doing it look like? Well, we could do a bit of a funky check here. We could add some if statements here by checking, okay, if type of error is an object and type of error is not null, right?
02:20 Because of course, null in TypeScript is like a type of object. And there is a error property or a message property on the error. Then we can console log error.message. We know that it is basically a message, right? Okay, this is pretty gross.
02:38 And this does work, you know, outside of this, like we're going to get the error off. Error is unknown here. And it is going to effectively check if this object has a message property and then console log it. And then if we don't, for instance, if we get the wrong thing,
02:54 then we're going to throw the actual error that came up. If, for instance, we did do math.random here, then we would actually get to throw the error up there. In fact, no, because it would probably have a message property, so it would still be console logged. But it's good practice just to make sure that everything possible is handled inside your catch. So this is pretty gross.
03:13 How can we improve on this? Well, we can say if error instance of error. And now what we're doing here is we're saying, okay, if error is assignable to the error class, then we've got our console log error.message. This is a really, really nice little thing to bear in mind
03:31 because it just gives you a one-liner that you can use to say, okay, if I've got my error, then I'm going to do this with it. Fantastic. And if we were to throw different types of errors, like type error or syntax error, for instance, then they all extend the base error class.
03:47 This means that syntax error is still an instance of error, which is good. So there we go. This is basically a really nice way that you can handle try-catches. And just know that if this error comes up in the future, this x is of type unknown, then you can usually narrow your way out of it.
04:07 Or if you have some complex narrowing, then you can use Zod instead. And if you have questions about Zod, you can see the free Zod tutorial that's on this site.