-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
What’s going on with Object.keys
?
#6927
Comments
Object.keys is actually implemented internally using a special type constructor that basically ignores the libdef definitions. Why it's done this way, I don't know, but I'd bet the definition can be removed. |
Why is it implemented using an internal type constructor? If there's something special about |
Lovely. Can we provide a typing that approximates the behavior (probably
Indeed, even using |
Lines 6061 to 6076 in 60fdca2
Looks like |
Is it unsound? |
Yes: function test(val: { foo: string }) {
Object.keys(val).forEach(key => {
val[key].toLowerCase();
});
}
test({ foo: 'x', bar: 123 }); |
This is not an issue with
|
Without any property lookups: type Foo = {foo: string};
function test(val: Foo): $Keys<Foo>[] {
return Object.keys(val);
}
test({ foo: 'x', b: 123 }); |
@dsainati1 what about this // @flow
type K = "foo" | "f";
class X {
foo: string
f() {}
}
function f(o: {foo: string, +f: () => void}) {
return Object.keys(o) // ["foo"]
}
(f(new X): Array<K>); // `string` should be incompatible with `K` |
Both of your examples make use of inexact objects, for which our contract is that they are sound up to The case is similar here, in that using the result of |
So if this concession is made for |
@dsainati1: Is this really considered wontfix given that it’s unsound // @flow
type T = { good: number };
function getKeys(val: T): $Keys<T>[] {
return Object.keys(val);
}
const keys: "good"[] = getKeys({ good: 1, bad: 2 });
for (const key of keys) {
if (key !== "good") {
// Unreachable according to Flow...
throw new Error((key: empty));
}
} (This typechecks, but of course errors at runtime.) Comments on #4527 from @vkurchatkin and @samwgoldman suggest that at |
@samwgoldman is working on a more comprehensive change to the object model that might subsume this issue, so I wouldn't say that this is a wontfix because as far as I am aware fixing these unsoundness issues around inexact object should by proxy fix the issue you are seeing with keys. Sam can probably give a more accurate description of what his work is about though. |
@samwgoldman can you explain what changes on object model would be made? |
These types would make sense declare function keys<T: $Exact<any>>(object: T): Array<$Keys<T>>;
declare function keys(object: mixed): Array<string>;
const x: {|
a: string
|} = {a: ''}
const xs = keys(x); // Array<$Keys<T>>
(xs[0]: 'b') // error: 'a' ~> 'b'
const y: {a: string} = {a: ''}
const ys = keys(y); // Array<string>
(ys[0]: 'b') // error: string ~> 'b' |
Some updates to my types for declare function keys<O: {| +[key: mixed]: mixed |}>(o: O): Array<string>;
declare function keys<T, O: $Exact<T>>(o: O): Array<$Keys<O>>;
declare function keys(o: mixed): Array<string>;
declare function values<O: {| +[key: mixed]: mixed |}>(o: O): Array<[string, mixed]>;
declare function values<T, O: $Exact<T>>(o: O): Array<$Values<O>>;
declare function values(o: mixed): Array<mixed>;
declare function entries<O: {| +[key: mixed]: mixed |}>(o: O): Array<[string, mixed]>;
declare function entries<T, O: $Exact<T>>(o: O): Array<[$Keys<O>, $Values<O>]>;
declare function entries(o: mixed): Array<[string, mixed]>; |
@goodmind so should I make a PR for these or will you? |
@jcready @dsainati1 doesn't like idea of exact constraint |
@goodmind @jcready doesn't like the idea of |
@jcready I agree with you, this was from Discord months ago when I proposed this libdef change |
Even if this doesn't get accepted as PR, people could still opt into this locally by redefining the |
We recently improved |
The core libdef for
Object.keys
is clear as can be:(
flow/lib/core.js
Line 58 in 172d28f
This suggests that the following code should have a type error:
And yet the above code typechecks. Why?
This was not always the case. The above code did not typecheck in Flow v0.59.0, but has typechecked since Flow v0.60.0. But the release notes for v0.60.0 don’t mention such a change.
Also, this does not work if the key type is opaque (possibly related to #6569 and friends):
Having the refined type is quite useful, though the grievances in #5210 (from before the change) are legitimate. What is going on? Whence the inconsistency between the libdef, the working example with a transparent subtype of
string
, and the failing example with an opaque subtype ofstring
?(Full Flow-Try here.)
The text was updated successfully, but these errors were encountered: