Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

in Typescript, try...catch error object shows "Object is of type 'unknown'.ts(2571)"

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)

enter image description here

enter image description here

Thanks for sharing Youtube video,

like image 809
stefancho Avatar asked Oct 03 '21 06:10

stefancho


People also ask

How do I fix object is type unknown error?

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!

What is unknown type in typescript?

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.

How do you use try catch in typescript?

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.

How do you handle TS errors?

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.


Video Answer


3 Answers

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.

like image 57
tsamridh86 Avatar answered Nov 03 '22 10:11

tsamridh86


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
}
like image 38
AJcodez Avatar answered Nov 03 '22 09:11

AJcodez


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:

OK method

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:

BEST method

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.

like image 33
iceblueorbitz Avatar answered Nov 03 '22 09:11

iceblueorbitz