Reputation: 13821
I am using TypeScript, and have a try / catch block. The caught error is typed (Error
). I would like to use the error's message
attribute.
I am using eslint with the @typescript-eslint/no-unsafe-assignment
rule enabled.
try {
throw new Error('foo');
} catch (err: Error) {
const { message }: { message: string } = err;
return {
message: `Things exploded (${message})`,
};
}
When I run my linter, I receive the following:
4:9 error Unsafe assignment of an any value @typescript-eslint/no-unsafe-assignment
This is confusing to me, since the error is typed (Error
).
How can I catch an error in TypeScript and access the Error's properties?
Upvotes: 91
Views: 90934
Reputation: 925
I'd suggest using the ternary operator with a type check. The accepted answer allows execution to continue in the case the error is not an Error (e.g. someone has decided to throw 42 or something equally valid yet unreasonable)
try {
throw new Error('foo');
} catch (e) {
const message = e instanceof Error ? e.message : "Unknown error."
return {
message
}
}
Upvotes: 14
Reputation: 131
Visit https://devblogs.microsoft.com/typescript/announcing-typescript-4-4/
" Using unknown in Catch Variables Users running with the --strict flag may see new errors around catch variables being unknown, especially if the existing code assumes only Error values have been caught. This often results in error messages such as:
Property 'message' does not exist on type 'unknown'. Property 'name' does not exist on type 'unknown'. Property 'stack' does not exist on type 'unknown'.
To get around this, you can specifically add runtime checks to ensure that the thrown type matches your expected type. Otherwise, you can just use a type assertion,
add an explicit : any to your catch variable, or turn off --useUnknownInCatchVariables."
you can simply add in tsconfig.json:
"compilerOptions": { "useUnknownInCatchVariables": false }
or you can use like:
catch (e: any) {console.log(e.message)}
Upvotes: 6
Reputation: 16604
TL;DR: I made a tiny library to make catching unknown
safely easy:
import { asError } from 'catch-unknown';
try {
throw new Error('foo');
} catch (err) {
return {
message: `Things exploded (${asError(err).message})`,
};
}
According to Typescript's lib.es5.d.ts
file, an Error
is any object with string properties called name
and message
and optionally stack
.
interface Error {
name: string;
message: string;
stack?: string;
}
In practice, errors are generally created using the Error
constructor (which is required for instanceof Error
to be true):
interface ErrorConstructor {
new(message?: string): Error;
(message?: string): Error;
readonly prototype: Error;
}
declare var Error: ErrorConstructor;
The catch-unknown
library provides two simple functions:
export declare function isError(err: unknown): err is Error;
export declare function asError(err: unknown): Error;
isError
is a typical type guard, which checks for the correct property types, as opposed to using instanceof
(for maximum compatibility). asError
is essentially isError(err) ? err : { name: typeof err, message: String(err) }
, plus handling of a few special cases. 99.99% of the time, values you catch
are already conform to Error
, so all that happens is a quick type check.
Upvotes: 5
Reputation: 2062
Since Typescript 3.7 you can make an assertion type guard
export function assertIsError(error: unknown): asserts error is Error {
// if you have nodejs assert:
// assert(error instanceof Error);
// otherwise
if (!(error instanceof Error)) {
throw error
}
}
} catch (err) {
assertIsError(err);
// err is now typed as Error
return { message: `Things exploded (${err.message})` };
}
Upvotes: 34
Reputation: 7660
TypeScript 4.0 introduced the ability to declare the type of a catch
clause variable... so long as you type it as unknown
:
TypeScript 4.0 now lets you specify the type of
catch
clause variables asunknown
instead.unknown
is safer thanany
because it reminds us that we need to perform some sorts of type-checks before operating on our values.
We don't have the ability to give caught errors arbitrary types; we still need to use type guards to examine the caught value at runtime:
try {
throw new Error('foo');
} catch (err: unknown) {
if (err instanceof Error) {
return {
message: `Things exploded (${err.message})`,
};
}
}
Upvotes: 145
Reputation: 625
The catch parameter type can only be any or unknown. I think in your case it's considered as any.
Is this working in your environment?
try {
throw new Error('foo');
} catch (err: unknown) {
const { message } = err as Error; // I removed : { message: string } because it should be infered
return {
message: `Things exploded (${message})`,
};
}
Upvotes: 5