Skip to content

Unnecessary any returned from Object.values and Object.entries #58376

Closed as not planned

Activity

  1. MartinJohns commented on Apr 30, 2024

    @MartinJohns
    Contributor

    This is working as intended.

    This is perfectly legal code, and a return type of (string | null)[] would be wrong:

    const o = { foo: 123, 5: 'test', 8: null };
    const i: I = o;
  2. typeholes commented on Apr 30, 2024

    @typeholes
    Author

    Scratch that, I see the issue and it should really be returning unknown[]

    I understand how that FAQ applies to Object.keys, but I do not think it applies here.

    const o = { foo: 123, 5: 'test', 8: null };
    const i: I = o;

    Here you have explicitly type i: I so I would expect that Object.values would return I[string|number][] (edit: per fatCerebus's correction)

    note that if you let i be inferred the result would be (string | number | null)[] which is also correct

  3. fatcerberus commented on Apr 30, 2024

    @fatcerberus

    Why would Object.values(i) return I[]? None of the properties are Is. The point Martin is making is that TS is only seeing the types here and there's no guarantee that a thing typed as I only has string | null values.

  4. MartinJohns commented on Apr 30, 2024

    @MartinJohns
    Contributor

    Scratch that, I see the issue and it should really be returning unknown[]

    Agreed, but that would be a very big breaking change for little gain.

    Here you have explicitly type i:

    That was intentional to demonstrate the point. You don't know where and how the object was created.

  5. fatcerberus commented on Apr 30, 2024

    @fatcerberus

    typeholes Just to be clear: From the type checker's point of view there's no difference between the explicitly typed o in your example and the explicitly typed i in Martin's. Returning (string | null)[] when Object.values is called on an I wouldn't be sound.

  6. typeholes commented on Apr 30, 2024

    @typeholes
    Author

    Interestingly we have the exact same issue with a record, but do get the specific result type

    type T = Record<number, string|null>
    interface I { [x: number]: string | null ; } 
    
    const o = { foo: 123, 5: 'test', 8: null };
    const oT: T = o; const ovT = Object.values(oT);  // (string | null)[]
    const oI: I = o; const ovI = Object.values(oI);  // any[]

    https://www.typescriptlang.org/play/?target=99&jsx=0&exactOptionalPropertyTypes=true&lib=lib.esnext.d.ts&ts=5.5.0-dev.20240429#code/PTAEAEBcEMCcHMCmkBcpEGcB2iAekAoECAGwEsAjNTHfAgsrSRWAM2gGNFQBJUAb1ABtXGiwBXALYUWAXTQZIsRvFAAfUBJIlQAblABfUPUgBPAA7cAKqAC8oAEqIOAe1gATADwTpLADSgispY8GpaJAB89K5YiqAudgKgrC4uaACMAEwAzAEArGgA5MyKhQEAHGLi2oa6BDFxLlZoNvYu+g2Q8QBuraAA8hQAVs6QAHTd0CTimAAUTQCU+qDEs0Eq6prVJAtCskRgoEfHJ6enAHoA-PUusV0uPGh8bR23jd3PA8OjE1MzGPMeEsjsRoFhTHsDmdoTCrvQCEA

  7. snarbies commented on Apr 30, 2024

    @snarbies

    Object.values has an overload for array-likes and string index signatures (but not bare numeric index signatures).

        values<T>(o: { [s: string]: T; } | ArrayLike<T>): T[];

    Supporting arrays makes sense. I'm guessing the string index signature is meant for object-as-a-dictionary scenarios. Either one has a potential hazard with excess properties, though.

  8. typeholes commented on Apr 30, 2024

    @typeholes
    Author

    Correct, so my question is should we be inconsistent between the interface and record values. I think consistency would be better, especially when you consider that any is just explicitly unsound.

  9. snarbies commented on May 2, 2024

    @snarbies

    It's not really inconsistent though. Index signatures and interfaces represent different things.

    Index signatures are "arrayish." They may not necessarily have numeric keys, but they represent collections of key/value pairs where the "element" values all conform to a single type, so it makes sense for Object.values to support this scenario.

    By its very nature, an interface is not exhaustive. An interface represents a subset of an object's actual shape (or a commonality among otherwise differently-shaped objects). So even if Object.values returned a union of known property types based on the interface, you would still have to include unknown in the union to account for properties not identified by the interface. And if you include unknown in the union, then the entire thing reduces to unknown.

    That it returns any instead of unknown is unfortunate, but it's due to historical reasons.

  10. typeholes commented on May 3, 2024

    @typeholes
    Author

    Implementation is in #58358

  11. typescript-bot commented on May 6, 2024

    @typescript-bot
    Contributor

    This issue has been marked as "Question" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

  12. locked as resolved and limited conversation to collaborators on Oct 22, 2025
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

    QuestionAn issue which isn't directly actionable in code

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions