首页 > 解决方案 > 为什么 Typescript 将 `object.hasOwnProperty("key")` 视为与 `"key" in object` 本质上不同

问题描述

declare const action: { total: number } | { };
declare const defavlt: 200;

const total = (action.hasOwnProperty("total")) ? action.total : defavlt;

导致以下 TS 错误action.total

Property 'total' does not exist on type '{ type: "NEW_CONVERSATION_LIST" | "UPDATE_CONVERSATION_LIST_ADD_BOTTOM" | "UPDATE_CONVERSATION_LIST_ADD_TOP"; list: IDRArray<RestConversationMember>; total: number | undefined; } | ... 13 more ... | { ...; }'.
  Property 'total' does not exist on type '{ type: "UPDATE_URL_STATE"; updateObj: IMessagingUrlState; }'.ts(2339)

然而

const total = ("total" in action) ? action.total : defavlt

作品。TS 是否有理由以不同的方式对待这两种情况?

标签: typescript

解决方案


在问题microsoft/TypeScript#10485中,建议in操作员充当可用于过滤联合的类型保护;这是在microsoft/TypeScript#15256中实现的, 并与 TypeScript 2.7 一起发布

这不是为Object.prototype.hasOwnProperty(); 如果你真的对此有强烈的感觉,你可能想提出一个建议,注意到一个类似的建议 (microsoft/TypeScript#18282)被拒绝了,因为它要求的是更有争议的缩小而不是对象.. . 有些人两者都想要 (microsoft/TypeScript#20363)。并且不能保证该建议会被接受。

不过,幸运的是,您不必等待它在上游实施。与类似 的运算符不同in,该hasProperty()方法只是一个库签名,可以更改为充当用户定义的类型保护函数。更重要的是,您甚至不必接触标准库的定义;您可以使用声明合并Object来使用您自己的签名来扩充接口hasOwnProperty()

// declare global { // need this declaration if in a module
interface Object {
  hasOwnProperty<K extends PropertyKey>(key: K): this is Record<K, unknown>;
}
// } // need this declaration if in a module

该定义表明,当您检查 时obj.hasOwnProperty("someLiteralKey")true结果意味着obj可分配给{someLiteralKey: unknown},而false结果则不能。这个定义可能并不完美,并且可能有很多边缘情况(例如,应该obj.hasOwnProperty(Math.random()<0.5?"foo":"bar")暗示什么?应该obj.hasOwnProperty("foo"+"bar")暗示什么?他们会做奇怪的事情)但它适用于您的示例:

const totalIn = ("total" in action) ? action.total : defavlt; // okay
const totalOwnProp = (action.hasOwnProperty("total")) ? action.total : defavlt; // okay

好的,希望有帮助;祝你好运!

链接到代码


推荐阅读