The following code block produces a typescript error, because although we know that foo[k]
and bar[k]
are the same type, TS cannot know (well, maybe by some magic it could, but apparently it doesn't)
interface IMixed {
a: number;
b: string;
c: boolean;
}
const foo: IMixed = { a: 1, b: 'one', c: true };
const bar: IMixed = { a: 2, b: 'two', c: false };
(Object.keys(foo) as Array<keyof IMixed>).forEach(k => {
foo[k] = bar[k];
// ^^^^^^ Type 'string | number | boolean' is not assignable to type 'never'.
});
When TS can't figure something out that I know to be true, I cast it. But in this case, although I know this is a valid assignment, I don't know which type it is ... all I know is they are the same. I couldn't quite work out an elegant way to use a generic here.
Assume I am committed to the practice of iterating over these properties (say, because we expect properties do be added down the line, and similar code exists all over the codebase ...)
In short: How do I assert that this assignment is valid?
(side question: why does the error refer to the assignee as type 'never'?)
UPDATE
@captain-yossarian makes a good point about mutability, and presented a totally viable immutable solution. I feel that the question is still open, though, if the example is made a little tricker:
interface IMixed {
a: number[];
b: string[];
c: boolean[];
}
const foo: IMixed = { a: [1], b: ['one'], c: [true] };
const bar: IMixed = { a: [2], b: ['two'], c: [false] };
function intersection<T>(...arrays: T[][]): T[] {
return [...new Set([].concat(...arrays))]
}
(Object.keys(foo) as Array<keyof IMixed>)
.reduce((acc, elem) => ({
...acc,
[elem]: intersection(foo[elem], bar[elem])
// ^^^^^^^^^ Argument of type 'string[] | number[] | boolean[]' is not
// assignable to parameter of type 'string[]'.
}), foo);
The point is, assignment obviously requires that the types be compatible ... but so does this intersection
function. So this is like the immutable version of the original question.