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.hasOwn #58877

Closed
lishaduck opened this issue Jun 16, 2024 · 4 comments
Closed

Improve Object.hasOwn #58877

lishaduck opened this issue Jun 16, 2024 · 4 comments

Comments

@lishaduck
Copy link

⚙ Compilation target

esnext

⚙ Library

esnext

Missing / Incorrect Definition

Object.hasOwn:

Requested: hasOwn(o: object, v: PropertyKey): boolean;hasOwn<T extends object = object>(o: T, v: PropertyKey): v is keyof T>;

To my understanding, a type predicate only asserts one way, so this should be sound even if there are excess properties.
As far as I can tell, this shouldn't be breaking.

Right now, I have a helper function that asserts this, but copying it into every project gets annoying and repetitive.

Sample Code

const a = {
  foo: 1
} as const;
const b: string = 'foo';
if (Object.hasOwn(a, b)) {
  console.log(a[b]);
}

Documentation Link

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn

@whzx5byb
Copy link

I don't think it's a good idea.

function Object_hasOwn<T extends object = object>(o: T, v: PropertyKey): v is keyof T {
    return Object.hasOwn(o, v);
}

declare const x: { a: 1 } | { b: 2 }
const prop: string = 'a';

if (Object_hasOwn(x, prop)) {
    prop
  // ^? prop: never... what?
}

@lishaduck
Copy link
Author

lishaduck commented Jun 16, 2024

Hmmm.
I can reproduce that, and I don't know why it does that.
Well, it seems this won't work after all.

I don't think it's a good idea.

function Object_hasOwn<T extends object = object>(o: T, v: PropertyKey): v is keyof T {
    return Object.hasOwn(o, v);
}

declare const x: { a: 1 } | { b: 2 }
const prop: string = 'a';

if (Object_hasOwn(x, prop)) {
    prop
  // ^? prop: never... what?
}

If you use a const instead of a declare const, it works. Strange.

function Object_hasOwn<T extends object = object>(
  o: T,
  v: PropertyKey
): v is keyof T {
  return Object.hasOwn(o, v);
}

const x: {a: 1} | {b: 2} = {a: 1};
const prop: string = 'a';

if (Object_hasOwn(x, prop)) {
  prop;
  // ^? prop: "a"
}

@MartinJohns
Copy link
Contributor

Duplicate of #44253.

@lishaduck lishaduck closed this as not planned Won't fix, can't repro, duplicate, stale Jun 16, 2024
@fatcerberus
Copy link

To my understanding, a type predicate only asserts one way

For future reference, this is not true. x is T means that if the predicate is true, x is indeed a T, but also if it's false, x is not a T, and the compiler will narrow accordingly. Refer to #15048.

Even with one-way type predicates, this wouldn't be sound for excess properties: Object.hasOwn(foo, 'd') where keyof typeof foo is 'a' | 'b' | 'c' would return true, but 'd' is very much not a member of 'a' | 'b' | 'c'.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants