TypeScript Version: 3.5.0-dev.20190514
Search Terms:
fromEntries
Code
// A *self-contained* demonstration of the problem follows...
// Test this by running `tsc` on the command-line, rather than through another build tool such as Gulp, Webpack, etc.
type T = {}
const a: { [key: string]: T } = { "key": {} }
const b: [string, T][] = Object.entries(a)
const c : {[key: number]: T; [key: string]: T } = Object.fromEntries(b)
const d : { [key: string]: T } = Object.fromEntries(b)
Expected behavior:
~Compilation should fail because number is not a key of a.~
_edit:_ The return type of Object.fromEntries(b) should be inferred as d: { [key: string]: T } .
Actual behavior:
_edit:_ The return type of Object.fromEntries(b) is {[key: number]: T; [key: string]: T } Adding an erroneous [key: number].
Object.fromEntries has type:
fromEntries<T = any>(entries: Iterable<readonly [PropertyKey, T]>): { [k in PropertyKey]: T };
However if I make a second type parameter for fromEntries which extends PropertyKey and use that for the key type in forEntries:
fromEntries<K extends PropertyKey, T = any>(entries: Iterable<readonly [K, T]>): { [k in K]: T };
I'm willing to make a PR for this if this sounds like a reasonable approach.
Playground Link:
Apologies, but I could not use es2019 in the playground.
Related Issues:
https://github.com/microsoft/TypeScript/pull/30934
https://github.com/microsoft/TypeScript/issues/30933 https://github.com/microsoft/TypeScript/issues/25999
Which line are you expecting to fail? c? Why?
Sorry @RyanCavanaugh I didn't explain my test case correctly.
Even with the desired behaviour I think c would compile. I've edited my case to show what I expect to happen more clearly. (Namely the return type of Object.fromEntries taking the key type information from the tuple array instead of PropertyKey)
@RyanCavanaugh Would you mind if I attempted this and raised a PR please?
I believe this is also causing an error in our project. I have distilled the error down to the following simple repo which I think is the same issue @AWare is describing here.
package.json:
{
"name": "tserror",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"build-good": "rimraf index.js && tsc --build tsconfig.good.json",
"build-bad": "rimraf index.js && tsc --build tsconfig.bad.json"
},
"devDependencies": {
"typescript": "^3.5.2",
"rimraf": "^2.6.3"
}
}
tsconfig.good.json:
{
"compilerOptions": {
"lib": ["esnext"],
"strict": true,
"keyofStringsOnly": false,
},
"include": ["index.ts"],
"exclude": ["node_modules"]
}
tsconfig.bad.json:
{
"compilerOptions": {
"lib": ["esnext"],
"strict": true,
"keyofStringsOnly": true,
},
"include": ["index.ts"],
"exclude": ["node_modules"]
}
index.ts:
const moo: string = "oh oh"
When I run npm run build-good this simple example builds as expected. However, if I then go and run npm run build-bad I get the following error:

The only difference in the "bad" build is that i've set "keyofStringsOnly": true instead of false. This is a stripped down version of my actual configuration but I believe it is just the interaction of "keyofStringsOnly":true and lib:["esnext"] that is relevant to this issue.
Having same issue on my side, same configuration ("keyofStringsOnly": true)
Got the same issue randomly, not sure what triggered it as things were compiling fine. But using a target of either esnext or es2019 will cause this compilation issue. Also confirmed this by updating to typescript@next and running the same code.
Replication details are as follows, I did this in my /tmp directory and you can pretty much copy and paste these steps to reproduce.
tsconfig.json
{
"compilerOptions": {
"target": "es2019",
"keyofStringsOnly": true,
},
}
Running tsc will complain that it has nothing to do, so touch index.ts.
Then running the tsc command will result in the following error:
*chop*/typescript/lib/lib.es2019.object.d.ts:28:81 - error TS2322: Type 'string | number | symbol' is not assignable to type 'string'.
Type 'number' is not assignable to type 'string'.
28 fromEntries<T = any>(entries: Iterable<readonly [PropertyKey, T]>): { [k in PropertyKey]: T };
~~~~~~~~~~~
Found 1 error.
This is specifically part of es2019 (currently esnext aliases this I believe).
The fix, for now, is to "target": "es2018" instead in your tsconfig.json.
{
"compilerOptions": {
"target": "es2018",
"keyofStringsOnly": true,
},
}
I changed target to "target": "es2018", but I am still getting the following error:
node_modules/typescript/lib/lib.es2019.object.d.ts:28:81 - error TS2322: Type 'string | number | symbol' is not assignable to type 'string'.
Type 'number' is not assignable to type 'string'.
28 fromEntries<T = any>(entries: Iterable<readonly [PropertyKey, T]>): { [k in PropertyKey]: T };
Here is my tsconfig.json
{
"compilerOptions": {
"target": "es2018",
"moduleResolution": "node",
"module": "commonjs",
"sourceMap": true,
"rootDir": "src",
"outDir": "dist",
"keyofStringsOnly": true,
"lib": ["esnext", "dom"],
"esModuleInterop": true,
},
"exclude": ["node_modules", "**/*.spec.ts"]
}
The only thing that works is removing "keyofStringsOnly": true,.
I am not sure why this is still happening. Has anyone else experienced this/ any suggestions on a fix?
It sounds like there are really two bugs here: the more serious is that the current definition of fromEntries errors when keyofStringsOnly: true. The cause (and likely fix) is similar to the original bug so I'm not going to open a new bug, though.
I see 3 possible fixes for these two bugs:
K extends PropertyKey. This is what @Aware suggests, but it doesn't fix the second bug.string instead of PropertyKey. This fixes both bugs raised here, but may break other code. The related issues the OP links have some discussion of this.Because this isn't a 3.8 regression, I'm not going to fix this in the 3.8 beta period — any change to the DOM types is going to break someone. I'll look at this when 3.9 starts.
Is there a particularly good workaround for this in the meantime without setting keyofStringsOnly: false?
*edit - for now I have set "skipDefaultLibCheck": true which seems okay
My question is this: I want Object.fromEntries() to be generic on the key type, something like this:
fromEntries = <Key extends PropertyKey, Value>(entries: Iterable<[Key, Value]>) => { [key: Key]: Value }
Is there a motivation for why a more precise typing like that would be unsafe at the library level? I use enums a lot as keys and I'd like for mapping across them to preserve the fact that the keys are from my enum, not just a string or maybe a number or something.
I opened #37457 which makes the return type { [k: string]: T }. Compared to the current declaration, this just drops the numeric index signature.
Most helpful comment
Got the same issue randomly, not sure what triggered it as things were compiling fine. But using a target of either
esnextores2019will cause this compilation issue. Also confirmed this by updating totypescript@nextand running the same code.Replication details are as follows, I did this in my
/tmpdirectory and you can pretty much copy and paste these steps to reproduce.tsconfig.json
Running
tscwill complain that it has nothing to do, sotouch index.ts.Then running the
tsccommand will result in the following error:This is specifically part of
es2019(currentlyesnextaliases this I believe).The fix, for now, is to
"target": "es2018"instead in yourtsconfig.json.