Cómo evitar la clave dinámica del error de asignación de objeto en TypeScript

8

Digamos que tenemos un código TypeScript que se ve así:

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

Esto da error

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

para la declaración

user1[key] = user2[key];

Si cambiamos la definición de keysa

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

el error desaparece, pero perdemos la seguridad del tipo.

¿Hay alguna forma de evitar este error sin dejar de mantener la seguridad de tipo?

Vaibhav K
fuente
1
let keys: string[]elimina el error pero introduce otro, ya que TS no puede garantizarlo user1[key]o user2[key]es válido.
VLAZ
@VLAZ Sí, let keys: string[]presenta otro problema, pero sería un error en tiempo de ejecución (si la matriz contiene un nombre de campo no existente como valor). Lo que estamos buscando es una verificación en tiempo de compilación para evitarlo.
Vaibhav K

Respuestas:

5

No hay una buena manera de evitar una aserción de tipo aquí. En una versión reciente en TS (creo que la publicación 3.5) al escribir a través de un índice, el valor escrito debe ser compatible con todos los valores de propiedad posibles especificados por la clave. En su caso, eso sería lo number & stringque reduce a neverpor lo tanto el error.

La causa raíz es que TS no realiza un seguimiento de las variables solo de los tipos, por lo que, en lo que respecta a los tipos, su ejemplo no sería 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]

La solución más simple es usar una aserción de tipo si, como en su caso, está seguro de que esto está bien:

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
}

Jugar

Alternativamente (pero ya no es seguro), puede usar una pequeña escapatoria donde T[K]se puede asignar un valor de í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])
}

Jugar

Tiziano Cernicova-Dragomir
fuente
Gracias por la respuesta. Aunque no es ideal, as neverfunciona. Además, ¿podría explicar el setmétodo en el segundo ejemplo?
Vaibhav K