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,完全违反类型定义:
因此TypeScript抛出2322赋值错误不是推断bug,是正常的安全校验机制。type Item = { name: string; foo: 1 | 2; bar: number }; const items: Item[] = [{ name: 'test', foo: 1, bar: 3 }]; update(items, 'foo', 'bar');
正确实现
固定两个属性参数版本(匹配给出的调用示例)
直接将数组元素类型明确约束为包含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
相关产品推荐
相关产品推荐

