interface Student {
id: number
name: string
age: number
}
let alice: Student = { name: "alice", age: 19, id: 1 }
const bob: Student = { name: "Bob", age: 19, id: 2 }
const keys: (keyof Student)[] = ["age", "name"]
for (let c of keys) {
alice[c] = bob[c] // here shows the error during compile
/*
src/index.ts:11:3 - error TS2322: Type 'string | number' is not assignable to type 'never'.
Type 'string' is not assignable to type 'never'.
*/
}
How to edit the code so that not typscript error will occur during alice[c] = bob[c]
?
Currently when you write code of the form alice[c] = bob[c]
, the compiler only analyzes the types of the keys and not their identities. That means it can't tell the difference between alice[c] = bob[c]
and alice[c1] = bob[c2]
where c1
and c2
are both the same type as c
. One might think this is fine and that both sides of the assignment are of the indexed access type Student[keyof Student]
.
But TypeScript treats indexed access writes differently when the key is a union type like keyof Student
. This safety feature was implemented in microsoft/TypeScript#30769, and it does catch legitimate problems, since alice[c1] = alice[c2]
really isn't safe; what if c1
is "id"
but c2
is "name"
, for example?
But it also prohibits some perfectly safe code. For alice[c] = bob[c]
we know that the c
on each side isn't just of the same type, it's the identical value. There's a feature request to support this sort some "same-key" assignment at microsoft/TypeScript#32693, but it hasn't been implemented yet.
Until and unless it is implemented, you'll need to refactor to a version where the compiler sees the two sides of the assignment as identical or compatible. According to a comment on microsoft/TypeScript#30769 you can get this if k
is of a generic type.
Therefore the easiest approach here is to put the assignment inside a generic function... and the easiest way to do that is probably to change from a for...of
loop to the forEach()
array method, which takes a callback:
keys.forEach(<K extends keyof Student>(c: K) => {
alice[c] = bob[c]
});
Now c
is of generic type K
, and both sides of the assignment are seen as the generic indexed access type Student[K]
, which is allowed.