首页 > 解决方案 > Typescript 无法通过 hasOwnProperty 类型缩小为对象键赋值

问题描述

hasOwnProperty用类型缩小定义:

function hasOwnProperty<
  Obj extends Record<string, any>,
  Prop extends PropertyKey,
>(
  obj: Obj,
  prop: Prop,
): obj is Obj & Record<Prop, any> {
  return Object.prototype.hasOwnProperty.call(obj, prop);
}

我希望这样一个简单的功能可以工作:

function addProp<T>(obj: Record<string, any>, key: string, val: any) {
  if (!hasOwnProperty(obj, key)) {
    obj[key] = val;
  }
}

但是,我越来越Type 'any' is not assignable to type 'never'.

TS游乐场

这种是有道理的,因为类型缩小是说不key存在于obj. 但是,这不应该阻止我添加密钥。有什么我做错了吗?

编辑: 另外,它在函数之外也不起作用:

const obj: Record<string, number> = {};
const key = 'key';
if (!hasOwnProperty(obj, key)) {
  obj[key] = 123;
}

标签: javascripttypescript

解决方案


function hasOwnProperty<
  Obj extends Record<string, any>,
  Prop extends PropertyKey,
>(
  obj: Obj,
  prop: Prop,
): obj is Obj & Record<Prop, any> {
  return Object.prototype.hasOwnProperty.call(obj, prop);
}


function addProp<T>(obj: Record<string, any>, key: string, val: any) {
  if (!hasOwnProperty(obj, key)) {
    obj // <---------------- is infered to never
    obj[key] = val;
  }
}

在您的情况下,obj条件语句推断为never. 为什么?

因为 type ofkey是一个字符串,!hasOwnProperty(obj, key)意味着obj没有更多的string键。

如何解决?

您可以为key参数添加显式泛型。

function addProp<T, Prop extends string>(obj: Record<string, any>, key: Prop, val: any) {
  if (!hasOwnProperty(obj, key)) {
    obj[key] = val;
    return obj
  }
  return obj
}


const x = addProp({}, 'a', 42) // Record<string, any> & Record<"a", any>

在这里,在我的博客中,您可以找到更多关于 typescript 突变的问题

@captain-yossarian 的答案适用于我可以为密钥提供泛型的功能。但是,在我不能使用泛型的地方,它仍然不起作用(见编辑)

这个例子是行不通的:

const obj: Record<string, number> = {};
const key = 'key';
if (!hasOwnProperty(obj, key)) {
  obj[key] = 123;
}

既然你假设key属性是可选的obj,你应该使用Partial

const obj: Partial<Record<PropertyKey, number>> = {};
const key = 'key';
if (!hasOwnProperty(obj, key)) {
  const x = obj
  obj[key] = 123; // ok
}

推荐阅读