TypeScript is all about type safety. But there are a number of situations that TypeScript can't properly guard against without a little help.
Take Exceptions for example. When we wrap a try .. catch
around some code to handle a potential exception we probably want to do something like this:
try {
await this.userService.all();
} catch (e: Error) {
this.errorService.handle(e);
}
Unfortunately TypeScript won't allow you to do this!
The issue is that in JavaScript (and TypeScript) anything can be thrown as an "error". You can throw any arbitrary type; a number
, a string
, even null
.
This is totally valid JavaScript and TypeScript:
throw 42;
Which means that our try .. catch
only has two options for the type hint here. any
or unknown
.
The any
type is the escape hatch out of TypeScript's type safety so we generally want to avoid that if we can help it.
So we're left with unknown
.
The unknown
type in TS is a way to force users of the type, to narrow the type down to something specific. It's not valid to use something when the type is unknown
so we're forced to write a Type Guard.
Here is the try .. catch
from above rewritten to use unknown
:
try {
await this.userService.all();
} catch (e: unknown) {
if (e instanceof Error) {
// guaranteed to be an Error now!
this.errorService.handle(e);
} else {
throw Error("Can't handle this error!");
}
}
There is a cleaner way to do this though. What if the handle
method dealt with the unknown
type instead?
class ErrorService {
handle(e: unknown) {
let message: string;
if (e instanceof Error) {
message = e.message;
} else if (typeof e === 'string') {
message = e;
} else {
message = "We don't know what the heck happened -- sorry!"
}
// handle the error message here
}
}
This way the try..catch
outside can just pass the unknown
error through like this:
try {
await this.userService.all();
} catch (e: unknown) {
this.errorService.handle(e);
}
Like what you read? Want to support me?
A cup of coffee goes a long way 🙏
Why not buy me one? https://ko-fi.com/danjfletcher
Top comments (0)