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

Object.values and Object.entries return type any when passing an object defined as having number keys #26010

Closed
philraj opened this issue Jul 27, 2018 · 4 comments · May be fixed by #58358
Closed
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@philraj
Copy link

philraj commented Jul 27, 2018

TypeScript Version: 3.1.0-dev.20180727

Search Terms: Object.values Object.entries wrong type inference any

Code

interface I {
  [x: number]: string | null;
}

const o: I = { 5: 'test', 8: null };
const v = Object.values(o);

Expected behavior: Type of v should be (string | null)[].

Actual behavior: Type of v is any[].

Playground Link: Typescript Playground doesn't support Object.values().

Related Issues: #21089

This bug seems to occur due to the type definitions for ObjectConstructor, which used to include the possibility of number keys in their type signatures. I'm not sure exactly why this was changed to ArrayLike to resolve the related issue I linked (this is the exact PR), but it breaks the type inference on the results of Object.values() and Object.entries() when passing it a type defined as having number keys.

interface ObjectConstructor {
    /**
     * Returns an array of values of the enumerable properties of an object
     * @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
     */
    values<T>(o: { [s: string]: T } |  ArrayLike<T>): T[];

    // ...
}

This type signature used to be values<T>(o: { [s: string]: T } | { [n: number]: T }): T[], and with this older signature, the example code properly types the result of Object.values().

@mhegazy
Copy link
Contributor

mhegazy commented Jul 27, 2018

It was originally defined to allow numeric index signatures, but that broke enums (#21089 as you have referenced earlier). I do not think there is a good solution that does not break something.

@mhegazy mhegazy added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Jul 27, 2018
@philraj
Copy link
Author

philraj commented Jul 30, 2018

What is the reason for the wrong type inference on enums when the type signature allows numeric indexes?

[edit] Removed invalid code example.

@philraj
Copy link
Author

philraj commented Jul 31, 2018

In #21089 you wrote:

one option is to give enums a string indexer to string | number in addition to the number indexer, but I am not sure i understand what that would affect...

Is this too dangerous to attempt? Shouldn't enums have that by default, since numeric enums have the property of reverse mappings? I don't understand the following discrepancy:

enum E { A, B }
const fakeEnum = {
  0: 'A',
  1: 'B',
  A: 0,
  B: 1,
};

const v1 = Object.values(E); // v1 has type any[]
const v2 = Object.values(fakeEnum); // v2 has type (string|number)[]

@quicksnap
Copy link

I just encountered this any inference when migrating some types that were used with Object.entries, resulting in returning [string, any][].

We aim to have no any in our codebase, as it leads to avoidable runtime errors. If there's no resolution to this, we'll likely enforce usage of a wrapper utility to provide better typings.

Would love to see progress on this.

Thank you to the TypeScript team for all your hard work! <3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants