11

假设我们的 TypeScript 代码如下所示:

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) {
  user1[key] = user2[key];
}

这给出了错误

Type 'string | number' is not assignable to type 'never'.

对于声明

user1[key] = user2[key];

如果我们改变keysto的定义

let keys: string[] = ["id", "name"];

错误消失了,但我们失去了类型安全。

有什么方法可以避免这个错误,同时仍然保持类型安全?

4

2 回答 2

8

这里没有避免类型断言的好方法。在 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])
}

于 2019-11-01T09:54:11.490 回答
-1

您必须使用实用函数来帮助 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])
}

我创建了一个游乐场演示供您使用。

如果没有该函数,则将user2[key]其解析为接口值中所有可能类型的交集User,并且由于对象不能同时是数字字符串,因此您最终会得到never. 并且never对于 User 中的任何字段(或 TS 中的其他任何内容)都不是有效类型。

另请参阅关于查找类型的 Typescript 2.1 部分

于 2021-01-23T18:12:15.087 回答