Techniques for Coercing an Unknown Type
As mentioned, there are several ways to solve this problem.
Use Any Type
One way to solve this challenge is to type e
in the catch
as any
:
} catch (e: any) {
return e.message;
}
We can use any
because we're pretty confident that the error is going to be an error.
What makes this solution not ideal is that we lose autocomplete on the e
. This makes it easy to end up with typos that can cause issues later.
This isn't the solution I would suggest.
Coerce as Error
A slightly better solution would be to coerce e
to be an Error
, and return the message:
} catch (e) {
return (e as Error).message;
}
Here e
is still unknown
when we enter the catch
, but using as
coerces it to an Error
and we get our autocomplete as expected.
This solution is about as unsafe as the one above - we're not checking if e
is an Error, we're casting it as an error.
Check with instanceof
This solution is pretty similar to the last, except this time we'll use instanceof
to see if e
is an Error
:
} catch (e) {
if (e instanceof Error) {
return e.message;
}
}
Unlike previous solutions, this time we're checking e
at runtime to whittle down what it could be.
We're not blanketing it with the any
type and we're not casting it to something else either.
This is my recommended solution, because it's the safest.
If you get something that isn't an error, it will fall down to the next block where you could do something else with it.
However, depending on the constraints of your codebase and how much you care about avoiding any
s, you may prefer one of the other solutions.
Transcript
This one has several solutions. Here, we're just doing it as an any. We can use any in this position because we're pretty confident that the error is going to be an error. It's not the ideal solution because this e here, we lose all autocomplete on it and it's very, very easy to do something like this and not notice it, and have a typo here and we don't even realize it, which would make the test fail.
Any, it's fine if you want to use it and it's very, very low impact here. It's just going to be within this block. If you really want to do it right, then I wouldn't suggest doing any.
The next way to do it is pretty similar, I would say, is you've got e as error inside here. E is unknown here. Then you coerce it to say, "e is an error," and then you extract the message from it. Here, we're not saying it's any here. We do get things from it. We get message, we get name, we get stack. If I add a typo here, then it's going to alert me. This is a little bit better.
Technically, if this block was longer and more complicated, which yours in production are usually going to be, then e might not be an error. You might end up with someone accidentally saying, "Throw failure," in which case, you're going to return undefined here. This is not the perfect solution either, but it's better than the previous one.
This one is a better solution. What we're doing here is we're saying e is unknown in this position, but we're checking e and checking if it's an instance of error here. This is like the typo that we saw earlier. That instance of is then going to whittle down what that error can be. We've got e.message, e.name, e.stack.
Unlike the previous ones, we are actually checking at runtime what that type is and doing something based off that. We're not casting it using the as that we saw before, and we're not blanketing over it with any, which we also had before.
This is my recommended solution. It's also pretty tight. If you get something that isn't an error, then it's going to fall down into this block, and you might need to do something else with it. This is probably the safest approach. Depending on the constraints of your code base, your time constraints, and how much you care about avoiding anys, then you might want to prefer one of the other solutions.