【问题标题】:TypeScript error handling an unknown variable: Property does not exist on type 'object'处理未知变量的 TypeScript 错误:“对象”类型上不存在属性
【发布时间】:2021-08-27 23:57:38
【问题描述】:

我正在尝试了解如何正确处理 unknown 变量,该变量可能是具有某些我可以使用的属性的对象。这源于使用仍在开发中的第三方库时的各种 try/catch 场景,因此“e”可能是也可能不是具有某些属性的对象。

以下是我的问题的简化版本,但该问题还有其他表现形式。因此,我希望避免类型断言或禁用类型检查。我正在寻找的是一种检查“未知”值以查看它是否是具有某些属性的对象的正确方法。

如果条件不充分,为什么会出现以下情况? TypeScript 只是将其视为未知对象,并没有使用我传递的信息来确保 (1) 它是一个 object,(2) 它是 not null ,并且 (3) 它有键“errNum”

在所有这些检查之后,还有哪些其他可能的情况会导致 "console.log(e.errNum)" 在运行时抛出错误?这在 VSCode GUI 和使用 webpack 5 构建时都会失败(在最近从 tslint 迁移后使用 eslint)。我想不出任何其他会在运行时导致问题的边缘情况。

try {
    const err = { errNum: 12 };
    throw err;
} catch (e) {
    // e is inferred as unknown, as it should be
    if (e && typeof e === 'object' && 'errNum' in e) console.log(e.errNum); // TS error: Property 'errNum' does not exist on type 'object'.
}

【问题讨论】:

  • 尽可能避免过多的类型断言。例如,以下内容会起作用,但会变得非常冗长:else if (e) errorMessage += `\n\n${typeof e === 'object' && (e as AnyObject)?.message ? (e as AnyObject).message : (e as AnyObject).toString()}`;

标签: typescript


【解决方案1】:

您将无法利用类型检查器的推理功能,因为 catch 子句变量是未知的。在这种情况下,更容易的事情可能是使用条件来确定 errNum 是类型保护函数中的属性。

catch (e)
{
  if (isErrorNumObj(e)) console.log(e.errNum);
}

function isErrorNumObj<T>(obj: T): obj is T & { errNum: unknown } {
  return obj && 'errNum' in obj;
}

您甚至可以制作一个更通用的版本,可以接受任意数量的道具。

try {
  const err = { errNum: 12, stackTrace: 'abc' };
  throw err;
} catch (e: unknown) {

  if (hasProps(e, 'errNum', 'stackTrace')) {
    console.log(e.errNum, e.stackTrace);
  }
}

function hasProps<T, U extends string | number | symbol>(obj: T, ...propName: U[]): obj is T & { [P in U]: unknown } {
  return obj && propName.every(x => x in obj);
}

【讨论】:

    【解决方案2】:

    在 typescript 中,in 运算符可用作类型保护,以允许编译器缩小联合类型中的类型:https://www.typescriptlang.org/docs/handbook/2/narrowing.html#the-in-operator-narrowing

    但是,在您的情况下,e 被推断为不是联合类型的object 类型,因此即使在应用errNum in e 之后,它仍然是object 类型,而不是被推断为@987654328 @

    您可以做的是定义一个自定义错误类,如下所示:

    class MyError extends Error {
      constructor(readonly errNum: number) {
        super()
      }
    }
    

    用它来构造你的错误:

    throw new MyError(10)
    

    然后使用instanceof缩小其类型:https://www.typescriptlang.org/docs/handbook/2/narrowing.html#instanceof-narrowing

    catch (e) {
      if (e instanceof MyError) {
        console.log(e.errNum)
      }
    }
    

    【讨论】:

      【解决方案3】:

      我会创建一个用户定义的type predicate

      function isErrNum(x: any): x is { errNum: number } {
          return !!x && 'errNum' in x;
      }
      
      try {
          const err = { errNum: 12 };
          throw err;
      } catch (e: unknown) {
          // e is inferred as unknown, as it should be
          if (isErrNum(e)) {
              console.log(e.errNum);
          }
      }
      

      我们将“测试”代码提取到返回“变量是类型”的函数中。然后一个“真”返回通知编译器,在它为真的上下文中,对象是声明的类型,而 不是else 中的那个类型。

      TypeScript Playground

      【讨论】:

        猜你喜欢
        • 2020-10-27
        • 1970-01-01
        • 2020-09-02
        • 2023-02-21
        • 2021-06-19
        • 2020-08-07
        • 2021-11-16
        • 2019-04-12
        • 2020-07-09
        相关资源
        最近更新 更多