Skip to content

Add stricter Object.fromEntries for constructing from const tuples - #50203

Closed
Bazyli Brzóska (niieani) wants to merge 4 commits into
microsoft:mainfrom
niieani:patch-1
Closed

Bazyli Brzóska (niieani) wants to merge 4 commits into
microsoft:mainfrom
niieani:patch-1

Conversation

@niieani

@niieani Bazyli Brzóska (niieani) commented Aug 6, 2022 •

Copy link
Copy Markdown

Related issue: #50379.

UPDATE: The version in this branch is unsound, but see the last comment here with a proposed solution that is sound.


Object.fromEntries as is declared today results in a loss of key names and a unionization of all values, or a complete loss of information and type of any.
This change makes it so that it is possible to create strict objects from const tuples.

const obj1 = Object.fromEntries([
  ['1', 2],
  ['3', 4],
] as const)

// now results in an object of type: {1: 2, 3: 4}, previously { [k: string]: 2 | 4 }

const obj2 = Object.fromEntries([
  ['1', 2],
  ['3', '4'],
] as const)

// now results in an object of type: {1: 2, 3: '4'}, previously any

Fixes #35745, #49305, #43332

`Object.fromEntries` as is declared today resulted in a loss of key names and a unionization of all values, or a complete loss of information and type of `any`. 
This change makes it so that it is possible to create strict objects from const tuples.

```ts
const obj1 = Object.fromEntries([
  ['1', 2],
  ['3', 4],
] as const)

// now results in an object of type: {1: 2, 3: 4}, previously { [k: string]: 2 | 4 }

const obj2 = Object.fromEntries([
  ['1', 2],
  ['3', '4'],
] as const)

// now results in an object of type: {1: 2, 3: '4'}, previously any
```
@typescript-bot

Copy link
Copy Markdown
Contributor

It looks like you've sent a pull request to update our 'lib' files. These files aren't meant to be edited by hand, as they consist of last-known good states of the compiler and are generated from 'src/lib' or possibly our lib generator. Unless this is necessary, consider closing the pull request and sending a separate PR to update 'src/lib' or https://github.com/microsoft/TypeScript-DOM-lib-generator

@typescript-bot TypeScript Bot (typescript-bot) added lib update PR modifies files in the `lib` folder For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Aug 6, 2022
@typescript-bot

Copy link
Copy Markdown
Contributor

The TypeScript team hasn't accepted the linked issue #35745. If you can get it accepted, this PR will have a better chance of being reviewed.

@Josh-Cena

Joshua Chen (Josh-Cena) commented Aug 6, 2022 •

Copy link
Copy Markdown
Contributor

@niieani

Bazyli Brzóska (niieani) commented Aug 6, 2022 •

Copy link
Copy Markdown
Author

Joshua Chen (@Josh-Cena) that's unfortunate. Is there any way to only target const tuples in the type system, without affecting mutable Arrays?

EDIT: ooh, what if I did check for extends Array<any> ? never : ... ?

@Josh-Cena

Copy link
Copy Markdown
Contributor

No, you can only target fixed length tuples—for example, add ten overloads for [[K, V]], [[K, V], [K, V]], etc. But there's not type to represent "any fixed-length tuple"

@sandersn

Copy link
Copy Markdown
Member

Given that the design is still in flux, I'd recommend opening a new issue to continue discussion. Then this PR should be either a draft or closed.

Only create strict types from const tuples, since we cannot guarantee the existence of any key in an iterable.
@niieani

Copy link
Copy Markdown
Author

Nathan Shively-Sanders (@sandersn) Joshua Chen (@Josh-Cena) I've addressed the issue. Here's a playground showing that it works correctly in all cases.
With the updated typings, only objects created from const tuples will have their shape known, as those are guaranteed to be present and thus sound.

Non-const tuples and regular arrays fallback to the old (currently released) behavior, i.e. they're either a Record if values are of the same type, or any.

@sandersn

Copy link
Copy Markdown
Member

Can you still open an issue to track this? All the issues you reference are closed, which makes me think that they're missing something new about what you've done. In particular, I'd like to see Ryan Cavanaugh (@RyanCavanaugh) 's opinion about your last comment on #35745 .

@niieani

Copy link
Copy Markdown
Author

Nathan Shively-Sanders (@sandersn) I can create an issue. I've discovered an unsoundness in my last implementation.
Let me see if I can fix it first, and I'll get back to you and ping Ryan.

@niieani

Bazyli Brzóska (niieani) commented Aug 20, 2022 •

Copy link
Copy Markdown
Author

Okay, I've solved the soundness issue, but it turned out to be a bit complex.

Here's the playground.

Nathan Shively-Sanders (@sandersn) Ryan Cavanaugh (@RyanCavanaugh) Perhaps there's an easier way to type this? Would anyone have suggestions on how to simplify this?

I'll hold off on updating the PR with the sound solution, until I get a green light.

EDIT: Opened #50379.

@niieani

Bazyli Brzóska (niieani) commented Feb 8, 2023 •

Copy link
Copy Markdown
Author

Just FYI for anyone following, I've finally gotten around to publishing an NPM package with the strict Object.fromEntries typings. It doesn't suffer from any unsoundness as far as I can tell and is fully compatible with the regular Object.fromEntries.
See nesity-types.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

For Uncommitted Bug PR for untriaged, rejected, closed or missing bug lib update PR modifies files in the `lib` folder

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

Support known possible keys in Object.entries and Object.fromEntries

5 participants