-
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
When using Object as maps, Object.values treats values as mixed #2221
Comments
Why does
|
I think this and #2174 are duplicates. @gabelevi Does what you're saying apply for map types, though? It seems like maps don't have this issue? As in the following gives me a type error on const a: {[key: string]: string} = {b: 3};
const b: {a?: string} = {b: 3}; Could this just be a matter of adding an overload for the case where the argument to |
Having this exact same problem right now. Use case: // types.js
export type FieldLabel =
'SOME_FIRST_LABEL'
| 'SOME_SECOND_LABEL'
// ...
;
// constants.js
import type { FieldLabel } from './types';
export type FieldLabels = { [key: FieldLabel]: FieldLabel };
export const FIELD_LABELS: FieldLabels = Object.assign({}, ...[
'SOME_FIRST_LABEL',
'SOME_SECOND_LABEL',
// ....
].map(label => ({ [label]: label })));
// elswhere, someFile.js
import type { FieldLabel } from '../types';
import { FIELD_LABELS } from '../constants';
const fieldLabels: Array<FieldLabel> = Object.values(FIELD_LABELS).sort(); Results in: |
Another very simple reproducible case, just in case it helps: https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoALgTwA4FMwCSAJmALxgDO6ATgJYB2A5gNwY74Byc6tUtAxgEMecemTABvVGDC0iALkJEANNLABbXBQqDGuRVTpNVAX1Zs8YAMrph+cgBIAogA9B-dAB4JAbQDWuJiKxAC6ilw8fEIi9CYAfOb8olSUtuj61mn2kmaoqEn0KfTcvALCtKLhJVHlouISsgpgAOQAjM3KGlo6eorNcLTNYLlUdj7FkWUxAHRyIeITpdEV9InJ6GCLNTEUVZPLoj7z5ADyAEYAVrge0wBugjAArloAFKPpAJSsQA I gave using $Exact<> an attempt here but looks like I might be using it wrong? |
When I try to use exact notation to map /* @flow */
const a: {|[string]: string|} = {'a': '1'};
const values: Array<string> = Object.values(a); I get these errors 3: const a: {|[string]: string|} = {'a': '1'};
^ property `a`. Property not found in
3: const a: {|[string]: string|} = {'a': '1'};
^ object type
4: const values: Array<string> = Object.values(a);
^ string. This type is incompatible with
[LIB] static/v0.42.0/flowlib/core.js:52: static values(object: any): Array<mixed>;
^ mixed Can you please tell me
|
Type declarations like |
@vkurchatkin Yes, that makes sense. But what how to deal with |
How the below should be written? Even when everything is exact, that doesn't work with 0.45.0
what we get:
This is causing headache too often. |
Any solution for this? Running into this all afternoon :( |
Just hit this too :( for (const [key, value] of Object.entries(obj)) { } is a very common code pattern that's really messed up by this. I've resorted to disabling flow on affected lines :( |
The reason this isn't supported was explained pretty simply by @vkurchatkin in my (now closed) PR:
My PR would've set the following function type signatures for - static entries(object: any): Array<[string, mixed]>;
+ static entries <T> (o: { +[s: string]: T }) : Array<[string, T]>;
+ static entries <T> (o: Array<T> | $ReadOnlyArray<T>) : Array<[string, T]>;
+ static entries (o: any) : Array<[string, mixed]>;
+ static keys <T> (o: { +[key: T]: any }): Array<T>;
static keys(o: any): Array<string>;
- static values(object: any): Array<mixed>;
+ static values <T> (o: { +[s: string]: T }) : Array<T>;
+ static values <T> (o: Array<T> | $ReadOnlyArray<T>) : Array<T>;
+ static values(o: any): Array<mixed>; |
@jcready that's not the case with exact types where we know exactly what the object contains. see my comment above #2221 (comment) It's not been only once or twice when someone in our dev team has spent way too much time figuring out what's wrong with their flow typings when this has been the reason. supporting exact types would help a bit. |
EDIT: this definition is also incorrect, as @jcready pointed out |
In a comment on another issue, @samwgoldman mentioned that support for better object-as-dictionary typings on exact objects was already his top priority. I think this issue might be closable since his work on that is already underway. |
Let's close it when it's actually fixed :P |
@villesau @TiddoLangerak the problem is that indexers in exact object types don't work as you'd expect (at least right now): declare class Obj {
static values <T> (o: {| +[s: string]: T |}) : Array<T>; // exact object w/ indexer
}
function test(val: {| foo: string |}) { // exact object w/o indexer
Obj.values(val).forEach((v) => {
v.toLowerCase();
});
}
test({ foo: 'bar' }); This results in the following error:
Alternatively if we use @TiddoLangerak's definition for static values<T, O : { +[string] : T }>(val : $Exact<O>) : Array<T>; Then type-at-pos reports that the Unrelated, but @asolove I really wish the flow team would stop closing issues before they've actually fixed the issue. There seems to be an odd treatment of issues in this repo where they will be closed when the issue has simply been triaged or someone says they are going to make said issue a priority. Issues should be referenced in commit/PR messages so they can be closed automatically when the code actually gets merged to master. |
You're right, I completely missed that |
See facebook/flow#2221 for false positives regarding Object.values
Is anyone looking at resolving this? |
Still facing the problem with 0.61.0 |
Actual in 0.63.1 |
@gabelevi: Any progress on this when working with exact types now that they are a thing? It is quite a ways since v0.32:
|
It makes sense to use ES6 Map objects for when you want a mutable map, rather than using objects for it. But I think it's fair to say that oldschool "objects-as-maps" serve some purpose still--for instance in APIs where an object is provided as a literal mapping keys to values, like The original issue is very old, and plenty of things have been added to Flow since... wouldn't it make for a modern libdef for That said, the current implementation of declare class Object {
static values<T: {}>(object: T): Array<$Values<T>>;
static values(object: mixed): Array<mixed>;
}
declare var test: {
name: string,
value: number,
};
const v = Object.values(x);
// type-at-pos says v has type Array<$Values<{name: string, value: number}>>
const w: Array<string | number> = v;
// produces: Cannot assign v to w because:
// • $Values [1] is incompatible with string [2] in array element.
// • $Values [1] is incompatible with number [3] in array element. Any eventual jankiness with |
@FireyFly This isn't sound class A {
x: number;
constructor(x: number) {
this.x = x;
}
f() {
return this.x;
}
}
type O = {+x: number, +f: () => number};
const test: O = new A(2);
((2): $Values<O>); // own property
((() => 2): $Values<O>); // not own property
console.log(Object.values(test)); // [2] |
Oh hmm... right, that's a good point, my bad. I guess the iterating over all properties rather than only own properties means we can't really do any better for a type for |
For sealed objects, which are very-well inferable, values/keys/entries should return sealed arrays, which also seem inferable. |
Nice. Was promised a better way and burden-free development, but seems like another day with more problems of trying to incorporate the Flow into a new project. Is anyone actually looking at this to provide some solutions or even, dare to say it, fix this? Today is the third anniversary of this issue. |
@laszukdawid yes, @goodmind has made sound versions of values/entries, but apparently this still isn't good enough for the flow maintainers. |
@jcready That's not a very generous interpretation. Why not jump on discord and discuss it with them? |
@lyleunderwood what would you consider a more generous interpretation? I asked if I should make a PR for @goodmind's proposed libdef changes. @goodmind said:
I attempted to find the conversation on discord and I believe this is it. @dsainati1's argument against it was:
My interpretation of this is basically that even though @goodmind's implementation using an exact constraint is sound in practice (or at least as sound as the current The most reasonable argument against the exact constraint implementation I've see was from @samwgoldman in this comment from January 2018:
And while I agree that the proto vs. own issue still isn't solved by @goodmind's exact constraint implementation, the fact is flow has this same issue with
|
The impression I got was that flow maintainers didn't want to make an API knowing they'll just have to change the API again down the road when the theoretically correct solution eventually comes along. For what its worth, if I could override the builtin libdef with @goodmind's exact constraint, I would have by now. |
Here's a workaround that uses type Line = { name: string };
type Table = { [id: string]: Line };
const table: Table = { 'random': { name: 'Test' } };
// Error.
Object.values(table).forEach((line: Line) => console.log(line.name));
// No error.
Object.keys(table).map(key => table[key])
.forEach((line: Line) => console.log(line.name)); |
The object.keys workaround is an old a know one, but I think that not using some of the best ES6 features because flow it's a bad thing to face. |
+1 on this issue. Still using the workaround. |
Closing in on 4 years on this issue. Any updates? It really is an inconvenience when you spend 2 minutes writing a reduce function on Object.values() and then another hour on trying to write it in a way that flow would allow when you know exactly what .values() should return. On top of that everyone on your team will ask for an explanation why you had to write it the way you did (see all the workaround ideas above), because it is counter-intuitive/ looks like bad practice (e.g. the |
Hey guys! Is cast to Can we do better than that? 😄 |
🎉 Happy Birthday!! 🥳🎂 |
Check for the type of the Object. In my case I accidentally set the type of the variable as If there's any condition associated inside the map function, we need to return some JSX for the callback or else It's always advisable to use a variable outside the
|
I think the assumption that solving this issue does not "align with [their] priorities" is safe. Or "sound", as the Flow team at Facebook would say. Let's close it. :) https://medium.com/flow-type/clarity-on-flows-direction-and-open-source-engagement-e721a4eb4d8b |
Is there any chance for this issue to be resolved? |
…ictionary (mixed otherwise) Summary: User request from Dec 2021, May 2021, Dec 2020, Apr 2020 For `Object.entries`, keys is typed exactly like it is for `Object.keys` For both, values is typed as such: - Dictionaries: the value of the dictionary - Objects & Instances: `mixed` - this avoids created non-performant union types Error diff analysis: - Previously, `Object.entries` returned `string` for the key type, rather than having the same behavior as `Object.keys`. We unsafely allow property read/writes with `string`. - Error code for suppressions has changed in some places from `incompatible-call` to `incompatible-use` - Many errors have simply moved (previously error on usage of `mixed`, now erroring on still invalid but more accurate type) After WWW diff D41527152, errors are only around ~500. Alternative 1 was to create a new utility type `$DictValues` and type the `Object.values` and `Object.entries` lib defs with this. Cons of this approach are introducing a new lib def, and the fact that `Object.entries` keys definition would differ from `Object.keys`, since `$Keys` and `Object.keys` are implemented differently. Changelog: [errors] Instead of `mixed`, type the result of `Object.values` and `Object.entries` on a dictionary to be the dictionary values, and `Object.entries` keys to behave like `Object.keys` Closes #2174, #2221, #4771, #4997, #5838 Reviewed By: jbrown215 Differential Revision: D35710131 fbshipit-source-id: 3163bc02dfc7cb70a5d6c0ba7da574a793672421
With above commit, object types that have an indexer and no explicit properties (e.g. |
I'm using objects as maps in the example below. It appears the type of Object.values is an array of mixed as opposed to an array of the Point type I defined.
It appears if I do use Object.keys instead, flow can correctly infer the type of foo[key]
This seems like a bug in flow, as we use Object as a map here and the type checker has enough info to correctly infer the type of Object.values(). Yes?
The text was updated successfully, but these errors were encountered: