Como evitar erro de atribuição de chave dinâmica de objeto no TypeScript

8

Digamos que tenhamos código TypeScript que se parece com:

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];
}

Isso dá erro

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

para a declaração

user1[key] = user2[key];

Se mudarmos a definição de keyspara

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

o erro desaparece, mas perdemos a segurança do tipo.

Existe alguma maneira de evitar esse erro enquanto ainda mantemos a segurança do tipo?

Vaibhav K
fonte
1
let keys: string[]remove o erro, mas introduz outro, já que TS não pode garantir isso user1[key]ou user2[key]é válido.
VLAZ
@VLAZ Sim, let keys: string[]introduz outro problema, mas isso seria um erro em tempo de execução (se a matriz contiver um nome de campo inexistente como valor). O que estamos procurando é uma verificação em tempo de compilação para evitá-la.
Vaibhav K

Respostas:

5

Não há uma boa maneira de evitar uma afirmação de tipo aqui. Na versão recente do TS (acho que no post 3.5), ao escrever um índice, o valor gravado deve ser compatível com todos os possíveis valores de propriedade especificados pela chave. No seu caso, isso seria o number & stringque reduz, neverportanto, ao erro.

A causa principal é que o TS não controla variáveis ​​apenas de tipos; portanto, no que diz respeito aos tipos, seu exemplo não seria diferente de:

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]

A solução mais simples é usar uma asserção de tipo se, como no seu caso, você tiver certeza de que está tudo bem:

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
}

Toque

Como alternativa (mas não mais um tipo seguro), você pode usar uma pequena brecha onde T[K]é atribuível ao valor do índice:

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])
}

Toque

Titian Cernicova-Dragomir
fonte
Obrigado pela resposta. Embora não seja o ideal, as neverfunciona. Além disso, você poderia explicar o setmétodo no segundo exemplo?
Vaibhav K