router.get('/cells', async (req, res) => {
try {
const result = await fs.readFile(fullPath, { encoding: 'utf-8' });
res.send(JSON.parse(result));
} catch (err) {
if (err.code === 'ENOENT') { // Object is of type 'unknown'.ts(2571) (local var) err: unknown
await fs.writeFile(fullPath, '[]', 'utf-8');
res.send([]);
} else {
throw err;
}
}
err.code
make this ts error : Object is of type 'unknown'.ts(2571)
I definitely know that err.code
exists, so I want to know how to define the type(or interface) of err
?
(tsc version info : My global typescript version is v4.3.5 and the above code belongs to my project which has typescript v4.1.2)
--- Edit ---
I finally know why this error happens to me. I thought I used tsc version under 4.3.x, but it turns out I used v4.4.x.
In vscode, cmd + shift + P
and search for Select Typescript version
and I actually used v4.4.3, (I mistakenly thought on version because I only check tsc version from terminal)
Thanks for sharing Youtube video,
The "Object is of type unknown" error occurs when we try to access a property on a value that has a type of unknown . To solve the error, use a type guard to narrow down the type of the object before accessing a property, e.g. if (err instanceof Error) {} . Copied!
unknown is the type-safe counterpart of any . Anything is assignable to unknown , but unknown isn't assignable to anything but itself and any without a type assertion or a control flow based narrowing. Likewise, no operations are permitted on an unknown without first asserting or narrowing to a more specific type.
In a try-catch statement, you code a try block that contains the statements that may throw an exception. Then, you code a catch block that contains the statements that are executed when an exception is thrown by any statement in the try block. This is known as exception handling.
The main code where an exception could arise is placed inside the try block. If an exception occurs, it goes to the catch block where it is handled; however, the catch block is skipped if no error is encountered. The finally block will always execute in any case, whether an error arises or not in the program.
Just very recently, Typescript has been update to make error object inside catch
be unknown
instead of any
which means, your code looks something like this to your compiler
catch(e: unknown) {
// your logic
}
Provide your own interface and save into another variable to avoid this error:
catch(e : unknown) {
const u = e as YourType
// your logic
}
You can still use any
there, but it's not recommended.
In JavaScript/TypeScript you can throw anything, not only errors. In theory it could be anything in the catch block. If you want to prevent the type error it could make sense to check if the unknown
value is a system error before checking the code.
if (err instanceof SystemError && err.code === 'ENOENT') {
// file not found
}
As stated in other answers, since TypeScript 4.4, errors are automatically cast as unknown, so you can't do anything with them without type checking. Unfortunately ErrnoExceptions are not implemented as an importable class, but rather just a regular Error with additional properties plugged in. It is a type interface in @types/node, but you can't use isinstance
to check against it since there's no class definition for this exact error, so checking isinstance
against Error
will not let you access the err.code
property. That being said, you can make the compiler happy with:
try {
await fs.readFile(file);
catch (err: NodeJS.ErrnoException) {
if (err?.code === 'ENOENT') return;
else throw err;
}
The caveat here is if you simply do if (err.code)...
and forget the ?
, the compiler won't complain, but you could potentially get a runtime error. This is highly unlikely unless err is null/undefined, but it's still not perfectly type safe since you could forget the ?
and get runtime errors in theory. The issue here is you're telling the compiler you know what the error is when in fact the error could be literally anything (which is the motivation to automatically cast errors as unknown).
You could also do catch (err: any)
but you won't get any type hints for the codes and you are still subject to the same issues if you forget the use the safe accessor on the code
property. There's not a particularly easy way around this since you cannot simply use safe accessor on unknown
types like this: if (err?.code === 'ENOENT') return;
. I'm not quite sure why and maybe they'll add this in a later realease, but either way, my favorite way to handle these fs errors is to write a typeguard helper function like so:
function isErrnoException(e: unknown): e is NodeJS.ErrnoException {
if ('code' in (e as any)) return true;
else return false;
}
And then your catch block like this:
try {
await fs.readFile(file);
} catch (err) {
// writing err.code here after the typeguard call satisfies the compiler and is SAFE because we already checked the member exists in the guard function.
if (isErrnoException(err) && err.code === 'ENOENT') return;
else throw err;
}
This checks at least the error object is ErrnoException-like in that it has a code
property. You could get more specific and test that ALL of the ErrnoException properties exist to really make sure it's an ErrnoException.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With