TypeScript 2.5 not inferring return type of reduce

Created on 8 Nov 2017  路  7Comments  路  Source: microsoft/TypeScript




TypeScript Version: 2.7.0-dev.201xxxxx

Code
This is a (reasonably) minimal example of the code I'm compiling. My actual function is filtering entries before reconstructing the object.

 export function foo<V>(object: { [key: string]: V }): { [key: string]: V } {
  const entries = Object.entries(object);
  const rv = entries.reduce((obj: { [key: string]: V }, [key, value]) => {
    obj[key] = value;
    return obj;
  }, {});
  return rv;
}

Note that the above code requires some options to use Object.entries and so you can reproduce the same error with the following code snippet if desired.

const entries: [string, V][] = []; // 
const rv = entries.reduce((obj: { [key: string]: V }, [key, value]) => {
  obj[key] = value;
  return obj;
}, {});

Expected behavior:
In TypeScript 2.4.2 the constant rv is correctly inferred to be of type { [key: string]: V } but it does not seem to be in TypeScript 2.5.3.

Actual behavior:
rv is inferred to be of type {}.

It doesn't seem like the definition of reduce has changed between versions. VSCode is jumping to this signature of reduce.

reduce<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U;

Obviously I can specify the return value of my function foo as I have and everything works, but previously I did not need to as it was correctly inferred.

Question

Most helpful comment

I think the problem is that {} is used for inference before the callback is. According to its declaration, reduce takes initialValue: U and returns a U, so there is no "need" to look at the callback to complete inference. If you write {} as { [key: string]: V } the example will work without needing a type annotation for obj.

All 7 comments

I think the problem is that {} is used for inference before the callback is. According to its declaration, reduce takes initialValue: U and returns a U, so there is no "need" to look at the callback to complete inference. If you write {} as { [key: string]: V } the example will work without needing a type annotation for obj.

Thanks for the quick reply, that was my best guess too. Specifying the type on {} works. Time for me to write a lint rule to find all the places I haven't done so.

Similar problem with reducing objects to array. Even specifying type on initialValue doesn't communicate to TS that I'm trying to produce an array.

        const arrInitialValue: Item[] = [];
        const _arroItems = arroResponses.reduce((acc, vResponse) => {
            let arrBills: Item[];
            let arrItems: Item[];
            const vItems: any = vResponse;

            if (Array.isArray(vItems)) {
                arrItems = Array.isArray(acc) && acc.concat(vItems);
            } else {
                arrBills = vItems.body;
                arrItems = arrBills.map(oRawBill: Item => {
                    return oRawBill;
                });
            }

            return arrItems;
        }, arrInitialValue);

        this.arrSomethingElse = _arroItems.map(foo);

The last line throws "[ts] Property 'map' does not exist on type '{}'."

@Vandivier That's not a complete example -- most of the identifiers in that code sample have no definition. When I add declare const arroResponses: string[];, then _arroItems is number[] and the error you mentioned doesn't appear.

@andy-ms I explicitly said my problem was "reducing objects to array"

arroResponses is an array of objects. In fact, it is an array of HTTP responses with different shapes. Clearly string[] is an inappropriate comparison. Why would I call vItems.body on a string?

You can see return arrItems and let arrItems: Item[];. Also, const arrInitialValue: Item[] = [];

Clearly the return type should be Item[]. What other information should I provide? The surrounding method is largely irrelevant.

@Vandivier Thanks, created an issue at #25454.

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

Was this page helpful?
0 / 5 - 0 ratings