Skip to content

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

Closed

Description

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().

Activity

  1. mhegazy commented on Jul 27, 2018

    @mhegazy
    Contributor

    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.

  2. philraj commented on Jul 30, 2018

    @philraj
    Author

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

    [edit] Removed invalid code example.

  3. philraj commented on Jul 31, 2018

    @philraj
    Author

    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)[]
  4. quicksnap commented on Feb 21, 2019

    @quicksnap

    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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Design LimitationConstraints of the existing architecture prevent this from being fixed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions