Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error object inside catch is of type unkown

I have the following code:

try {
  phpDoc(vscode.window.activeTextEditor);
} catch (err) {
  console.error(err);
  vscode.window.showErrorMessage(err.message);
}

however err.message gets the error Object is of type 'unknown'.ts(2571) on err., but I cannot type the object in catch (err: Error).

What should I do?

like image 357
Mathias Hillmann Avatar asked Jul 04 '21 00:07

Mathias Hillmann


People also ask

What is type of error in try catch TypeScript?

The try catch in TypeScript statement provides a way to handle some or all of the errors that may occur in an application. These errors are often referred to as an exception. In a try-catch statement, you code a try block that contains the statements that may throw an exception.

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.

What is unknown 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.


4 Answers

As a supplementary answer to CertainPerformance's one:

Up until TypeScript 4.0, the catch clause bindings were set to any thus allowing easy access to the message property. This is unsafe because it is not guaranteed that what's thrown will be inheriting from the Error prototype - it just happens that we don't throw anything but errors as best practice:

(() => {
    try {
        const myErr = { code: 42, reason: "the answer" };
        throw myErr; //don't do that in real life
    } catch(err) {
        console.log(err.message); //undefined
    }
})();

TypeScript 4.0 introduced an option for a safer catch clause by allowing you to annotate the parameter as unknown, forcing you to either do an explicit type assertion or, even better, to type guard (which makes the clause both compile-time and runtime-safe).

However, to avoid breaking most of the codebases out there, you had to explicitly opt-in for the new behavior:

(() => {
    try {
        throw new Error("ouch!");
    } catch(err: unknown) {
        console.log(err.message); //Object is of type 'unknown'
    }
})();

TypeScript 4.4 introduced a new compiler option called useUnknownInCatchVariables that makes this behavior mandatory. It is false by default, but if you have the strict option turned on (as you should), it is turned on which is most likely the reason why you got the error in the first place.

like image 191
Oleg Valter is with Ukraine Avatar answered Oct 22 '22 22:10

Oleg Valter is with Ukraine


If you don't want to change all your code after upgrading your TypeScript but are in strict mode, you can add the following compiler option after the strict option to overwrite it, as was hinted in Oleg's answer:

tsconfig.json

{
  "compilerOptions": {
    [...]
    "strict": true,
    "useUnknownInCatchVariables": false,
    [...]
    },
  },
}

"strict": true, sets useUnknownInCatchVariables to true, and then "useUnknownInCatchVariables": false, overrides that and sets it back to false.

like image 36
dast Avatar answered Oct 22 '22 22:10

dast


It's because anything can be thrown, hence unknown.

const fn = () => {
  throw 'foo';
};
try {
  fn();
} catch(e) {
  console.log(e);
  console.log(e instanceof Error);
  console.log(e === 'foo');
}

You'll need to check that the err actually is an error to narrow it down before accessing the message property.

try {
  phpDoc(vscode.window.activeTextEditor);
} catch (err) {
  console.error(err);
  if (err instanceof Error) {
    vscode.window.showErrorMessage(err.message);
  } else {
    // do something else with what was thrown, maybe?
    // vscode.window.showErrorMessage(String(err));
  }
}
like image 32
CertainPerformance Avatar answered Oct 22 '22 22:10

CertainPerformance


My TypeScript version is under 4.0, and I can't make it work again, then I created an auxiliar function to normalize the errors, like following:

interface INormalizedError {
  /**
   * Original error.
   */
  err: unknown;

  /**
   * Is error instance?
   */
  isError: boolean;

  /**
   * Error object.
   */
  error?: Error;

  /**
   * Call stack.
   */
  stack?: Error['stack'];

  /**
   * Error message.
   */
  message: string;

  toString(): string;
}

/**
 * Normalize error.
 *
 * @param err Error instance.
 * @returns Normalized error object.
 */
function normalizeError(err: unknown): Readonly<INormalizedError> {
  const result: INormalizedError = {
    err,
    message: '',
    isError: false,
    toString() {
      return this.message;
    }
  };

  if (err instanceof Error) {
    result.error = err;
    result.message = err.message;
    result.stack = err.stack;
    result.isError = true;
    result.toString = () => err.toString();
  } else if (typeof err === 'string') {
    result.error = new Error(err);
    result.message = err;
    result.stack = result.error.stack;
  } else {
    const aErr = err as any;

    if (typeof err === 'object') {
      result.message = aErr?.message ? aErr.message : String(aErr);
      result.toString = () => {
        const m = typeof err.toString === 'function' ? err.toString() : result.message;
        return (m === '[object Object]') ? result.message : m;
      };
    } else if (typeof err === 'function') {
      return normalizeError(err());
    } else {
      result.message = String(`[${typeof err}] ${aErr}`);
    }

    result.error = new Error(result.message);
    result.stack = aErr?.stack ? aErr.stack : result.error.stack;
  }

  return result;
}

An usage example:

try {
  phpDoc(vscode.window.activeTextEditor);
} catch (err) {
  const e = normalizeError(err);
  console.error(err);
  vscode.window.showErrorMessage(e.message);
}
like image 1
Eduardo Cuomo Avatar answered Oct 22 '22 23:10

Eduardo Cuomo