When you assign an object with empty array literals to a variable, the compiler may interpret those arrays as having the type never[]
, which isn't very useful:
const init = { one: [], two: [] };
/* const init: {
one: never[];
two: never[];
} */
init.one.push("whoops"); // error!
This limitation in TypeScript design has been noted in microsoft/TypeScript#29398. It seems that the compiler assigns a type too early to empty array literals, leading to unexpected behaviors at times.
If the compiler infers a type for a value that is not what you want, consider annotating or asserting the type of that value explicitly:
const init2: { one: string[], two: string[] } = { one: [], two: [] };
init2.one.push("okay"); // okay
In your code snippet using reduce()
, the compiler assumes the accumulator's type is {one: never[], two: never[]}
. This conflicts with the return type of the reducer callback, resulting in an error due to overloaded functions.
To resolve this issue, specify the generic type parameter explicitly when calling reduce()
:
const { one, two } = Object.values(fields).
reduce<{ one: string[], two: string[] }>( // <-- specify here
(acc, field) => {
if (isOne(field)) {
return { ...acc, two: [...acc.two, field] }
}
return { ...acc, one: [...acc.one, field] }
},
{ one: [], two: [] }
)
By setting the documented type of the accumulator to {one: string[], two: string[]}
, the call to reduce()
no longer throws an error because the return value matches the expected type.
I hope this explanation helps you make sense of the issue and good luck with your coding!
Playground link to code