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

TypeScript泛型约束对象键时属性赋值类型报错如何解决

问题原因

两次实现出现类型报错的核心原因是泛型约束不符合TypeScript的类型安全校验规则:

  • 第一次实现仅约束了泛型T包含name: string属性,完全没有限制传入属性键a/b对应值的类型,TypeScript无法确认T[A]/T[B]是可做乘法的number类型,既不允许直接做乘法运算,也不允许将number类型的乘积赋值给类型未知的row[a]。
  • 第二次实现虽然通过Entry类型约束了a/b对应的值是number,但额外引入的泛型T extends Entry<A,B>存在类型安全漏洞:T可以是Entry的子类型,T[A]可能是number的更窄子类型(比如字面量联合类型1 | 2)。如果允许直接给row[a]赋值通用number类型值,就可能出现非法赋值破坏类型约束,例如下方代码会把类型为1|2的foo赋值为3,完全违反类型定义:
    type Item = { name: string; foo: 1 | 2; bar: number };
    const items: Item[] = [{ name: 'test', foo: 1, bar: 3 }];
    update(items, 'foo', 'bar');
    
    因此TypeScript抛出2322赋值错误不是推断bug,是正常的安全校验机制。
正确实现

固定两个属性参数版本(匹配给出的调用示例)

直接将数组元素类型明确约束为包含name属性、且传入的属性键对应值均为number的结构,不额外引入可被窄化的子类型泛型,从根源避免赋值校验问题:

function update<K extends string>(
  data: Array<{ name: string } & Record<K, number>>,
  a: K,
  b: K
) {
  for (const row of data) {
    const result = row[a] * row[b];
    console.log(row.name, result);
    row[a] = result;
  }
}

支持任意多个属性参数版本(匹配“后续可传入一个或多个参数”的需求)

用剩余参数接收任意数量的属性键,默认将第一个传入的属性作为赋值目标,其余属性值相乘后写入目标属性:

function update<K extends string>(
  data: Array<{ name: string } & Record<K, number>>,
  ...keys: K[]
) {
  if (keys.length < 2) return;
  const [targetKey, ...multiplyKeys] = keys;
  for (const row of data) {
    const result = multiplyKeys.reduce(
      (current, key) => current * row[key],
      row[targetKey]
    );
    console.log(row.name, result);
    row[targetKey] = result;
  }
}
验证

以下调用均不会产生类型报错:

// 示例调用
update([{ name: 'X', percentage: 1, value: 2 }], 'percentage', 'value');
update([{ name: 'X', foo: 1, bar: 2 }], 'foo', 'bar');
// 多属性调用
update([{ name: 'Y', a: 2, b: 3, c: 4 }], 'a', 'b', 'c');

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 05:31:07