Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Object.keys(object) and Object.values(object) #26901

Closed
4 tasks done
KSXGitHub opened this issue Sep 5, 2018 · 5 comments
Closed
4 tasks done

Improve Object.keys(object) and Object.values(object) #26901

KSXGitHub opened this issue Sep 5, 2018 · 5 comments
Labels
Duplicate An existing issue was already created

Comments

@KSXGitHub
Copy link
Contributor

KSXGitHub commented Sep 5, 2018

Search Terms

Suggestion

When all keys and values of object is known

  • Object.keys(object) should return array type of union of known properties
  • Object.values(object) should return array type of union of known values

How to implement?

I tried creating the 2 functions:

declare function keys<
    Object extends { [_: string]: any },
    Key extends keyof Object
> (obj: Object): Key[]

declare function values<
    Object extends { [_: string]: any },
    Key extends keyof Object
> (obj: Object): Object[Key][]

I also tested them: playground link

Use Cases

Use with objects

const object = {
  abc: 123 as 123,
  def: 456 as 456
}

const objectKeys = Object.keys(object) // objectKeys: Array<'abc'|'def'>
const objectVals = Object.values(object) // objectVals: Array<123|456>

Use with enums

enum Foo {
  abc,
  def = 'def'
}

const FooKeys = Object.keys(Foo) // FooKeys: Array<'abc' | 'def'>
const FooVals = Object.values(Foo) // FooVals: Foo[]

Caveats

It is possible to ignore extra keys/values that don't exist in type definition

interface Foo {
  abc: 'abc' // the only known key/value pair of Foo
}

const bar = {
  abc: 'abc' as 'abc', // exist in Foo
  ghi: 'ghi' as 'ghi' // doesn't exist in Foo
}

const foo: Foo = bar
const keys = Object.keys(foo) // TS emits `keys: 'abc'[]` but the correct value is `['abc', 'def']`

Examples

See use cases and playground link above

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript / JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. new expression-level syntax)
@jack-williams
Copy link
Collaborator

Duplicate of this closed PR #25832.

As an example why:

const object = {
  abc: 123 as 123,
  def: 456 as 456
}

const objectKeys = Object.keys<{abc: 123}, 'abc'>(object) // objectKeys: Array<'abc'> whoops, array will contain 'def'

@Kingwl
Copy link
Contributor

Kingwl commented Sep 5, 2018

enum Foo {
abc,
def = 'def'
}

const FooKeys = Object.keys(Foo) // FooKeys: Array<'abc' | 'def'>
const FooVals = Object.values(Foo) // FooVals: Foo[]

keys of enum has not only "key"

@AlCalzone
Copy link
Contributor

While crawling through the issues I found this merged PR: #18175
Based on this precedence, would it make sense to type Object.keys as the union of all known properties and string[] for the unknown ones? Or is there simply no reasonable way to represent such a type?

@jack-williams
Copy link
Collaborator

@AlCalzone I think there is simply no way to represent this safely. Subtyping and this kind of reflection are incompatible because there is no correspondence between the value and the typescript type.

The reason getOwnPropertyDescriptors gets away with it is because you are immediately using the synthesised keys, and in the event the key doesn't exist, you can return a safe default value (PropertyDescriptor).

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Sep 17, 2018
@RyanCavanaugh
Copy link
Member

Dates back to at least #12253

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

5 participants