One form of Array.prototype.reduce is declared in lib.d.ts as:
/**
* Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
* @param callbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array.
* @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
*/
reduce(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U;
But when compiling (using tsc v1.7.3) the following code at the command line:
type test = {a: string, b: number};
let tests: test[] = [{a: "1", b: 1}, {a: "2", b:2}];
let result: number = tests.reduce((previousValue: number, currentValue: test): number => {
return previousValue + currentValue.b;
});
The following error results:
javascripts/test.ts(4,27): error TS2345: Argument of type '(previousValue: number, currentValue: { a:
string; b: number; }) => number' is not assignable to parameter of type '(previousValue: { a: string; b:
number; }, currentValue: { a: string; b: number; }, currentIndex:...'.
Types of parameters 'previousValue' and 'previousValue' are incompatible.
Type 'number' is not assignable to type '{ a: string; b: number; }'.
Property 'a' is missing in type 'Number'.
I would have expected the compiler to recognize that previousValue and the return value of the callback to be of type number.
This is actually correct. When you don't have the initial value, it would use the first element as the previous value and start at the second. It'll work if you start from zero, for instance:
tests.reduce((previousValue: number, currentValue: test): number => {
return previousValue + currentValue.b;
}, 0); // <- Note the initial value of zero.
If you run your previous code, you'll notice you get "[object Object]2" because of the type issue. When you start at zero like the above snippet, you'll get a number as expected.
@blakeembrey Using your suggestion to include the initial value worked and thanks. Yet requiring the passing of an initial value just to force the compiler to recognize an already explicitly stated intention (both previousValue and return type are explicitly typed as 'number') smells really bad to me. I'd love to see the compiler recognize this in a future release.
It's detected correctly. Please reread my above comment (I fixed some grammar) but it's not forcing the compiler to do anything, the compiler is correct. So is the type definition. Try running the JavaScript code and you'll probably see it easier, but this:
When you don't have the initial value, it would use the first element as the previous value and start at the second.
So when you're code is actually saying {a: "1", b: 1} + 2, which is wrong.
Yes you are correct and thank you but what I am saying is why cant the following be added:
reduce<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue?: U): U;
Notice the ? for initialValue making providing an initial value optional.
I see what you want. The definition itself is actually overloaded because the behaviour within the type system changes depending on whether or not that value is provided. If you look three lines above, the optional signature is the one you're having trouble with.
Reference: https://github.com/Microsoft/TypeScript/blob/master/lib/lib.d.ts#L1132-L1143
Yes, true. I realize now this isn't a bug in the sense of the type system. I will close this out myself. Have a a very happy holiday and thank you.
No worries, glad I could help :+1: Have a great holiday!
Most helpful comment
This is actually correct. When you don't have the initial value, it would use the first element as the previous value and start at the second. It'll work if you start from zero, for instance:
If you run your previous code, you'll notice you get
"[object Object]2"because of the type issue. When you start at zero like the above snippet, you'll get a number as expected.