Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeScript 2.5 not inferring return type of reduce #19823

Closed
aboyton opened this issue Nov 7, 2017 · 7 comments
Closed

TypeScript 2.5 not inferring return type of reduce #19823

aboyton opened this issue Nov 7, 2017 · 7 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@aboyton
Copy link

aboyton commented Nov 7, 2017

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.

@ghost
Copy link

ghost commented Nov 8, 2017

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.

@ghost ghost added Design Limitation Constraints of the existing architecture prevent this from being fixed and removed Design Limitation Constraints of the existing architecture prevent this from being fixed labels Nov 8, 2017
@aboyton
Copy link
Author

aboyton commented Nov 8, 2017

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.

@Vandivier
Copy link

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 '{}'."

@ghost
Copy link

ghost commented Jul 5, 2018

@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.

@Vandivier
Copy link

Vandivier commented Jul 5, 2018

@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.

@ghost
Copy link

ghost commented Jul 5, 2018

@Vandivier Thanks, created an issue at #25454.

@mhegazy mhegazy added the Question An issue which isn't directly actionable in code label Jul 17, 2018
@typescript-bot
Copy link
Collaborator

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

4 participants