catch 中的错误对象类型未知

Error object inside catch is of type unkown

我有以下代码:

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

但是 err.messageerr. 上收到错误 Object is of type 'unknown'.ts(2571),但我无法在 catch (err: Error) 中键入对象。

我该怎么办?

因为任何东西都可以抛出,所以unknown.

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

在访问 message 属性.

之前,您需要检查 err 实际上是一个错误以缩小范围
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));
  }
}

补充回答CertainPerformance's

在 TypeScript 4.0 之前,catch 子句绑定设置为 any,因此可以轻松访问 message 属性。这是不安全的,因为不能保证 抛出的内容将继承自 Error 原型 - 它只是 发生 我们不作为最佳实践,除了错误之外什么都不抛出:

(() => {
    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 一个更安全的 catch 子句选项,允许您将参数注释为 unknown,强制您进行显式类型断言,或者更好, 键入 guard(这使得该子句在编译时和运行时都是安全的)。

但是,为了避免破坏大部分代码库,您必须明确选择加入新行为:

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

TypeScript 4.4 引入了一个名为 useUnknownInCatchVariables 的新编译器选项,强制执行此行为。默认情况下它是 false,但是如果你打开了 strict 选项(正如你应该的那样),它就会打开,这很可能是你首先得到错误的原因。

如果您不想在升级 TypeScript 后更改所有代码但处于严格模式,则可以在 strict 选项后添加以下编译器选项以覆盖它,如 :

tsconfig.json

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

"strict": true,useUnknownInCatchVariables 设置为 true,然后 "useUnknownInCatchVariables": false, 覆盖它并将其设置回 false。

我的TypeScript版本在4.0下,我无法让它再次工作,然后我创建了一个辅助函数来归一化错误,如下所示:

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;
}

用法示例:

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

你不能在 typescript 中为 catch 子句变量编写特定的注释,这是因为在 javascript 中,catch 子句将捕获抛出的任何异常,而不仅仅是指定类型的异常。

在打字稿中,如果你只想捕获特定类型的异常,你必须捕获抛出的任何异常,检查它是否是你要处理的异常类型,如果不是,则再次抛出。

意思:在做任何事情之前先确保抛出的错误是一个axios错误。

解决方案 1

使用类型断言
使用 AxiosError 投射错误

import  { AxiosError } from 'axios';

try {
    // do some api fetch
    } catch (error) {
    const err = error as AxiosError
    // console.log(err.response?.data)
    if (!err?.response) {
        console.log("No Server Response");
    } else if (err.response?.status === 400) {
        console.log("Missing Username or Password");
    } else {
        console.log("Login Failed");
    }  
}
解决方案 2

首先检查错误是否是 axios 错误,然后再做任何事情

import axios from "axios"

try {
    // do something
} catch (err) {
    // check if the error is an axios error
    if (axios.isAxiosError(err)) {
        // console.log(err.response?.data)
        if (!err?.response) {
            console.log("No Server Response");
        } else if (err.response?.status === 400) {
            console.log("Missing Username or Password");
        } else {
            console.log("Login Failed");
        } 
    } 
}