Typescript: Empty type inferred by Object.entries

Created on 9 Feb 2018  ·  16Comments  ·  Source: microsoft/TypeScript

TypeScript Version: 2.7.1


Search Terms: Object entries empty TS2365

Code

let o: any = {x: 5};

let p = Object.entries(o).map(([k, v]) => v + 1);

Compile command: tsc --lib es2017,es2017.object a.ts

Expected behavior:
This should compile without any error, assuming v: any.
This was the case for Typescript 2.6.2 at least.

Actual behavior:
(3,43): error TS2365: Operator '+' cannot be applied to types '{}' and '1'.

Following codes compile without errors:

let o: object = {x: 5};

let p = Object.entries(o).map(([k, v]) => v + 1);
let o: any = {x: 5};

let p = Object.entries(o).map(([k, v]: [any, any]) => v + 1);
Bug lib.d.ts Fix Available Fixed

Most helpful comment

As a work-around, and building on the workaround for Object.keys() in this comment, I've just made one for Object.entries() too. Obviously only safe to use with objects that you have typed and know that these types can be relied upon.

export const entries = Object.entries as <T>(
  o: T
) => [Extract<keyof T, string>, T[keyof T]][]

__edit__: Looking at it more, I'm not sure Extract is required / desirable here. Seems to work fine without.

All 16 comments

Looks like this is due to #20553. Previously the second overload of entries took any, now it takes {}. Previously we would match the second overload while now we match the first.

declare function values<T>(x: { [key: string]: T }): T[];
declare function values(x: any): any;

declare function values2<T>(x: { [key: string]: T }): T[];
declare function values2(x: {}): any;

values(a)[0].toUpperCase(); // No error
values2(a)[0].toUpperCase(); // Error

This behavior seems sort of strange so I'm wondering if it's intended.

Talked with @sandersn and it looks like the problem would be fixed if we considered any to have an implicit index signature [key: string]: any in getImplicitIndexTypeOfType. (But we also have to make sure control flow gets there if the source type is any, so not quite as simple as that).

Currently the problem is that when choosing an overload we use a subtype relationship. any subtypes any but does not subtype {}, so after the change we end up choosing the first overload instead of the second.

Just jumping in here... I also get a problem with this code:

  const x: Readonly<{ [id: number]: number }>  = { 5: 1 }
  const entries = Object.entries(x)

It must be Readonly and have a numeric key.

entries is incorrectly typed as const entries: [string, {}][]

@rhys-vdw That looks like #22105 -- No problem with id: string.

I wounder if we should just get back to any. seems like our attempts to make it better have caused more trouble.

Actually my case was fixed in 2.8-rc but again happening in 2.8.1 .

Can this issue be expanded so Object.keys(o) and Object.entries(o) return type for the keys is keyof typeof o, rather than just string?

@EliSnow no, see #12253

So that you know, this is still an issue in 2.9 RC.

As a work-around, and building on the workaround for Object.keys() in this comment, I've just made one for Object.entries() too. Obviously only safe to use with objects that you have typed and know that these types can be relied upon.

export const entries = Object.entries as <T>(
  o: T
) => [Extract<keyof T, string>, T[keyof T]][]

__edit__: Looking at it more, I'm not sure Extract is required / desirable here. Seems to work fine without.

Can't reproduce this anymore, v is now any.

@liamness Shouldn't the TS implementation of keys, values and entries implement overload for readonly/const objects, that would return the type that can be statically obtained?

We still see this issue in TS 3.5

e.g.
type item = 'node' | 'link' | 'media'                
let typeDict: Record<item, string[]> = {
                    node: [],
                    link: [],
                    media: []
                };
Object.entries(typeDict).map(([_type, labels]) => {
           **_type inferred as string but not item here**
})

I think reverting #20553 is the right thing (@saschanaz it still repros for me on master). @DanielRosenwasser do you agree?

I think so too. It is very similar to how TS handles array[numberIndex] to not be undefined.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

DanielRosenwasser picture DanielRosenwasser  ·  3Comments

bgrieder picture bgrieder  ·  3Comments

fwanicka picture fwanicka  ·  3Comments

weswigham picture weswigham  ·  3Comments

MartynasZilinskas picture MartynasZilinskas  ·  3Comments