如何避免TypeScript中对象分配的动态键错误

如何避免TypeScript中对象分配的动态键错误,typescript,keyof,Typescript,Keyof,假设我们有这样的类型脚本代码: 类型用户={ 身份证号码:, 名称:string, } 让user1:User={id:123,name:“Hello”}; 让user2:User={id:456,name:“World”}; let键:(keyof User)[]=[“id”,“name”]; 对于(让钥匙中的钥匙){ user1[key]=user2[key]; } 这就产生了错误 Type 'string | number' is not assignable to type 'neve

假设我们有这样的类型脚本代码:

类型用户={
身份证号码:,
名称:string,
}
让user1:User={id:123,name:“Hello”};
让user2:User={id:456,name:“World”};
let键:(keyof User)[]=[“id”,“name”];
对于(让钥匙中的钥匙){
user1[key]=user2[key];
}
这就产生了错误

Type 'string | number' is not assignable to type 'never'.
为了声明

user1[key]=user2[key];
如果我们将
键的定义更改为

let键:字符串[]=[“id”,“name”];
错误消失了,但我们失去了类型安全性


在保持类型安全性的同时,是否有一些方法可以避免此错误?

这里没有避免类型断言的好方法。在TS上的最新版本中(我认为是3.5之后),当通过索引写入时,写入的值必须与键指定的所有可能的属性值兼容。在您的情况下,这将是
number&string
,它将减少为
never
,因此会出现错误

根本原因是TS没有只跟踪类型的变量,因此就类型而言,您的示例与以下示例没有区别:

let key1 = 'id' as  keyof User;
let key2 = 'name' as  keyof User;
//Obvious error
user1[key1] = user2[key2] // same error, TS can't distingusih between this and your user1[key] = user2[key]

最简单的解决方案是在以下情况下使用类型断言,就像在您的案例中一样:

type User = {
  id: number,
  name: string,
}


let user1: User = { id: 123, name: "Hello" };
let user2: User = { id: 456, name: "World" };
for (let key of keys) {
  user1[key] = user2[key] as never
}

或者(但不是任何类型安全的)您可以使用一个小漏洞,其中
T[K]
可分配给索引值:

type User = {
  id: number,
  name: string,
}


let user1: User = { id: 123, name: "Hello" };
let user2: User = { id: 456, name: "World" };

let keys: (keyof User)[] = ["id", "name"];

for (let key of keys) {
  set(user1, key, user2[key])
}

您必须使用一个实用函数,以帮助TypeScript相信该值是一个特定的、来自值类型并集的单一类型:

function setField<T, K extends keyof T>(o: T, key: K, value: T[K]) {
  o[key] = value
}

for (let key of keys) {
  setField(user1, key, user2[key])
}
函数设置字段(o:T,键:K,值:T[K]){
o[键]=值
}
对于(让钥匙中的钥匙){
设置字段(user1,key,user2[key])
}
我已经为你创造了一个游戏平台

如果没有该函数,
user2[key]
将被解析为
User
界面值中所有可能类型的交集,并且由于对象不能同时是数字和字符串,因此您将得到
永不
。而且,
never
对于User中的任何字段(或者TS中的任何其他字段)都不是有效的类型


另请参见。

let keys:string[]
确实删除了错误,但引入了另一个错误,因为not TS不能保证
user1[key]
user2[key]
有效。@VLAZ是的,
let keys:string[]
确实引入了另一个问题,但这将是一个运行时错误(如果数组碰巧包含一个不存在的字段名作为值)。我们正在寻找的是一个编译时检查以避免它。感谢您的响应。虽然不理想,
因为从来都不
工作。另外,您能否解释第二个示例中的
set
方法?您似乎忘了定义
set
。它可能是一个通用实用函数吗?