考虑使用以下类型谓词函数来判断对象是否包含特定键:

export function objectHasKey<T extends object>(
  obj: T,
  key: PropertyKey,
  includeKeysFromPrototype: boolean = false,
): key is keyof T {
  return includeKeysFromPrototype ? key in obj : Object.prototype.hasOwnProperty.call(obj, key);
}

现在,考虑下面的伪代码来说明我的困惑:

let entry: unknown = {
  someProperty: '123'
}

// (1) EXPECTED -> TS2339: Property 'someProperty' does not exist on type 'unknown'
console.log(entry.someProperty)

if (typeof entry === 'object' && objectHasKey(entry, 'someProperty')) {
  // (2) UNEXPECTED -> TS2339: Property 'someProperty' does not exist on type 'object'
  console.log(entry.someProperty)
}

尽管我已经使用类型谓词函数显式判断了somePropertyentry的键,但我不明白为什么TypeScrip会在(2)处引发错误.

推荐答案

您的功能

declare function objectHasKey<T extends object>(
  obj: T, key: PropertyKey, includeKeysFromPrototype: boolean
): key is keyof T

返回type predicate key is keyof T,这意味着当您调用objectHasKey(obj, key)并返回true时,key输入的表观类型将缩小为keyof T(相当于keyof typeof obj).obj输入的外观类型根本不会改变.因此,如果obj的类型是object,而key是类似"prop"的字符串文字,则true的结果意味着字符串文字"prop"将从"prop"缩小到keyof object,即the never type.这不是你的本意.即使这是您的意图,类型谓词也只用于缩小实际输入的范围.如果输入是类似"prop"的字符串文字,则将无法访问任何结果更改,因为您再也不会重复使用字符串文字.为了让它起作用,你需要使用variableproperty,比如const k = "prop",然后是objectHasKey(obj, k).

但不管怎样,这不是你的意图.您的代码假定在您调用objecthasKey(obj, key)并返回true之后,the 102 input的明显类型将发生变化,因此已知它将key作为键.这意味着您需要将类型谓词更改为obj is的形式,而不是key is.有一种方法可以做到这一点(有多种方法):

declare function objectHasKey<K extends PropertyKey>(
  obj: object,
  key: K,
  includeKeysFromPrototype: boolean = false,
): obj is Record<K, any>;

此函数为key的类型K中的generic.true的结果将objobject缩小到Record<K, any>(使用Record utility type).现在,您的代码可以正常工作了:

if (entry && typeof entry === 'object' && objectHasKey(entry, 'someProperty')) {
  console.log(entry.someProperty) // okay
}

Playground link to code

Typescript相关问答推荐

如何从TypScript中的接口中正确获取特定键类型的所有属性?

更新:Typescript实用程序函数合并对象键路径

如何通过TypeScript中的工厂函数将区分的联合映射到具体的类型(如类)?

在嵌套属性中使用Required

向NextAuth会话添加除名称、图像和ID之外的更多详细信息

缩小并集子键后重新构造类型

从子类构造函数推断父类泛型类型

有什么方法可以类型安全地包装import()函数吗?

常量类型参数回退类型

有没有一种方法可以确保类型的成员实际存在于TypeScript中的对象中?

(TypeScript)TypeError:无法读取未定义的属性(正在读取映射)"

专用路由组件不能从挂钩推断类型

在Cypress中,您能找到表格中具有特定文本的第一行吗?

与toast.error一起react 中的Async TUNK错误消息

将超类型断言为类型脚本中的泛型参数

使用ngfor循环时,数据未绑定到表

如何调整项目数组,但允许权重影响顺序

如何在TypeScript中将元组与泛型一起使用?

React-跨组件共享功能的最佳实践

NgRx 效果在 10 次错误后消失