You'll have to use a utility function, to help TypeScript trust that the value is a specific, single type from the union of value types:
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])
}
This tells the compiler that if the second argument for the function is a valid key for the type of the first, then the third argument must be a valid value for that key. If the first argument is a User instance, and the second is a valid key, then the last argument will be checked against typeof User[key], and otherwise you'll get an error.
I've created a playground demo for you to play with.
Without the function, user2[key] is resolved to be the intersection of all possible types in the User interface values, and since an object can't be a number and a string at the same time, you end up with never. And never is not a valid type for any of the fields in User (or for anything else in TS, for that matter).
Also see the Typescript 2.1 section on lookup types.
Note: the type system will not protect you from using a fixed value with a variable key, e.g. for (let key of keys) setField(user1, key, 'string') passes because the union of all acceptable values across all the keys includes both string and number.
let keys: string[]does remove the error but introduces another one, since not TS can't guarantee thatuser1[key]oruser2[key]is valid.let keys: string[]does introduce another issue, but that would be a run-time error (if the array happens to contain a non-existant field name as a value). What we're looking for is a compile-time check to avoid it.