Как избежать динамической ошибки назначения объекта в TypeScript - PullRequest
8 голосов
/ 01 ноября 2019

Допустим, у нас есть код 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];

Если мы изменимопределение от keys до

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

ошибка исчезает, но мы теряем безопасность типов.

Есть ли способ избежать этой ошибки при сохранении безопасности типов?

1 Ответ

5 голосов
/ 01 ноября 2019

Нет хорошего способа избежать утверждения типа здесь. В последней версии на 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
}

Play

В качестве альтернативы (но не более безопасного типа) вы можете использовать маленькую лазейку, где 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])
}

Play

...