You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript中使用keyof类型索引对象赋值的TS2322报错问题

解决TypeScript中可选属性对象的类型安全修改问题

问题背景

定义了包含可选属性的接口:

interface AnInterface {
  foo?: number;
  bar?: string;
  baz?: boolean;
}

尝试编写根据指定键修改对象值的函数时,直接执行output[keyVal.property] = keyVal.val会触发TS2322类型错误(如Type 'number' is not assignable to type 'undefined'),即使先判断val的类型也无法解决。但将属性值赋值给临时变量后再修改则不会报错,且不想通过添加索引签名(会放宽键的限制)来解决问题。

错误原因

TypeScript无法在keyVal为联合类型时,自动建立keyVal.property和keyVal.val的精确类型关联。比如在typeof keyVal.val === 'string'分支中,TS仅知道val是string,但无法确定property必然是对应类型的bar,因此会将output[keyVal.property]的类型推断为所有可选属性类型的模糊集合,导致赋值时类型不兼容。

解决方案

方案1:泛型函数(推荐)

通过泛型让TypeScript追踪keyVal的具体键类型,明确键与值的类型对应关系,实现完全类型安全:

function modifyObjectItem<K extends keyof AnInterface>(
  objectToModify: AnInterface,
  keyVal: { property: K; val: AnInterface[K] }
) {
  const output = { ...objectToModify };
  output[keyVal.property] = keyVal.val; // 无类型错误
  return output;
}

// 使用示例
const originalObj: AnInterface = { foo: 123 };
const updatedObj = modifyObjectItem(originalObj, { property: 'bar', val: 'hello' });

此方案无需额外类型判断,TypeScript会自动校验keyVal中键和值的类型匹配性。

方案2:自定义类型守卫

如果必须保留联合类型的keyVal参数,可通过自定义类型守卫在分支中窄化类型:

type AnInterfaceKeyVal = {
  [P in keyof AnInterface]: {
    property: P;
    val: AnInterface[P];
  };
}[keyof AnInterface];

// 自定义类型守卫
function isBarKeyVal(keyVal: AnInterfaceKeyVal): keyVal is { property: 'bar'; val: string } {
  return keyVal.property === 'bar';
}

function isFooKeyVal(keyVal: AnInterfaceKeyVal): keyVal is { property: 'foo'; val: number } {
  return keyVal.property === 'foo';
}

function isBazKeyVal(keyVal: AnInterfaceKeyVal): keyVal is { property: 'baz'; val: boolean } {
  return keyVal.property === 'baz';
}

function modifyObjectItem(
  objectToModify: AnInterface,
  keyVal: AnInterfaceKeyVal
) {
  const output = { ...objectToModify };
  if (isBarKeyVal(keyVal)) {
    output[keyVal.property] = keyVal.val;
  } else if (isFooKeyVal(keyVal)) {
    output[keyVal.property] = keyVal.val;
  } else if (isBazKeyVal(keyVal)) {
    output[keyVal.property] = keyVal.val;
  }
  return output;
}

通过类型守卫,TypeScript能在每个分支中确定keyVal的具体类型,从而允许安全赋值。

方案3:类型断言(简易场景)

在确保逻辑安全的前提下,直接使用类型断言跳过TypeScript的类型检查:

function modifyObjectItem(
  objectToModify: AnInterface,
  keyVal: AnInterfaceKeyVal
) {
  const output = { ...objectToModify };
  switch (typeof keyVal.val) {
    case 'string':
      output[keyVal.property as 'bar'] = keyVal.val;
      break;
    case 'number':
      output[keyVal.property as 'foo'] = keyVal.val;
      break;
    case 'boolean':
      output[keyVal.property as 'baz'] = keyVal.val;
      break;
    default:
      break;
  }
  return output;
}

注意:此方式需要开发者自行保证逻辑正确性,TypeScript不再做类型校验。

临时变量赋值不报错的原因

将output[keyVal.property]赋值给临时变量时,TypeScript会根据当前分支的val类型推断变量的具体类型(如string | undefined),而keyVal.val的类型(如string)是该变量类型的子集,因此赋值合法。但直接操作对象属性时,TS无法建立键与值的类型关联,导致类型不匹配错误。

内容的提问来源于stack exchange,提问作者user453445

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 13:03:27