属性'type'在类型'object'上不存在,即使经过`"type"in'的检查

回答 3 浏览 2098 2022-08-14

这段代码编译失败。

const nodeIsUseless = (node: unknown) =>
  node !== null &&
  typeof node === "object" &&
  "type" in node &&
  typeof node.type === "string" &&
  node.type === "JSXText";

因为在最后两行中:

Property 'type' does not exist on type 'object'.(2339)

...这本身我可以理解,但我不明白为什么在"type" in node检查之后,TS推断node仍然是object的类型,而不是{ type: unknown; [key: string]: unknown }的类型,这就不会触发错误

Nino Filiu 提问于2022-08-14
我想不出有什么办法可以在没有cast和运行时检查的情况下满足编译器对用户定义的类型保护,希望有比我更聪明的人看到这一点。我也意识到这并不是问题的关键所在,问题的关键在于推理。Jared Smith 2022-08-14
哦,看来连Matt Pocock都在为这个问题而苦恼 :/。Nino Filiu 2022-08-26
3 个回答
#1楼
得票数 4

不幸的是,TypeScript内置的in操作符类型防护并不像你所期望的那样强大。

从一个裸露的object,它将推断出被测试的属性是可用的。它可以推断出它确实存在,只有如果它是已经潜在可用的,例如,在一个联合的类型中。也就是说,控制流不会使被测试的属性 "出现",而只是试图区分联合的类型。

declare const o: object;
if ("type" in o) {
  o.type // Error: Property 'type' does not exist on type 'object'.
//^? object
}

declare const u: Number | String; // Note: using class only for the sake of the demo
if ("toFixed" in u) {
  u.toFixed(); // Okay
//^? Number
}

Playground 链接


在你的例子中,你可以为node参数指定一个联合体,其可能的类型是{ type: unknown }

然而,unknown顶级类型吸收了联合中的所有其他类型,所以它必须被其他所有类型所取代,例如使用特殊类型{}来代表普通类型。

const nodeIsUseless = (node: undefined | null | {} | { type: unknown }) =>
  node !== null &&
  typeof node === "object" &&
  //     ^? {} | { type: unknown } | undefined
  "type" in node &&
  //        ^? {} | { type: unknown }
  typeof node.type === "string" && // Okay
  //     ^? { type: unknown }
  node.type === "JSXText"; // Okay

Playground 链接

ghybs 提问于2022-08-15
#2楼 已采纳
得票数 4

虽然代码是正确的,但TypeScript不具备从"type" in node推断出{ type: unknown }的能力,但这个功能目前正在开发中


2022-09-21更新:随着上述PR的合并,现在可以在typescript@next中完成,应该很快就能在typescript@latest中使用。

另请参见关于meta的讨论,关于这个答案。

Nino Filiu 提问于2022-09-08
Nino Filiu 修改于2022-09-26
这个功能已经实现了。随着"改进运算符#50666"的合并,TypeScript确实有这个能力。A-Tech 2022-09-22
#3楼
得票数 2

你应该这样做。

const nodeIsUseless = (node: unknown) =>
  node !== null &&
  node !== undefined &&
  node instanceof Object &&
  !Array.isArray(node) &&
  "type" in node &&
  typeof node["type"] === "string" &&
  node["type"] === "JSXText";

只需要检查insonceof而不是typeof,并使用object["key"]方法来访问值而不是.key。 此外,确保项目不是数组是很好的做法,因为instonceof [] === 'object'也是如此。

Zack Mitkin 提问于2022-08-14
你是用什么版本的typescript做的?对我来说似乎并不适用。Nathan Chappell 2022-11-11