-
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
Potential for better map support in Object.values / Object.entries? #2174
Comments
Thanks for the suggestion! @samwgoldman WDYT? |
Yes, it would be very nice if Object.values no longer erases the types. Extra nice would be if it yielded a union of all of the values' types (e.g. The problem is when the object escapes; e.g. is given as an argument to a function that is not known to not mutate the argument, or is put in a variable that can be accessed from a different function scope. It becomes harder to guarantee that the object didn't get assigned an enumerable own property with a value of a different type. |
I want to improve the situation very much. Unfortunately, the solution isn't quite so simple.
We can have a more specific type for exact objects, but due to width subtyping not for annotations (except the new It would not be hard to build a CustomFunT (like Object$Keys) that handles exact objects and objects-as-map exactly but non-exact objects conservatively, however. |
Sorry for the noob question, but what's the recommended work around in cases where the author knows that the array won't be mutated and wants to use The best I've managed so far is this: global.objectValues = (map) => Object.values(map);
declare function objectValues<T>(object: {[key: string]: T}): Array<T>; but had originally anticipated being able to just use a typecast or something? |
|
Sorry for the very long delay here. This thread is long overdue for an update. One issue is exactness. An object type Another issue is that these APIs only work on "own" and "enumerable" properties. Flow does not currently track these attributes of object properties precisely. For example, An object type We don't have anything like exact object types for own-ness, but we are experimenting with a solution: exact object types should also imply that all properties are "own." This is actually pretty reasonable to do, because even the simple typing judgment With that in hand, if we enforce that an object type There are a few more details to work out, but that's the plan for now. Sorry to keep you all waiting for so long without an update. |
Thanks @samwgoldman for the update :) |
Thanks for the update! Is the best current solution to cast through |
@samwgoldman what's the latest on this? |
bump |
@samwgoldman finally got $Values in 0.50.0 🎆 |
I made it work like that :
see this comment: #4771 (comment) |
a flow-typed version of function entries<T>(obj : { [string]: T }) : Array<[string, T]> {
const keys : string[] = Object.keys(obj);
return keys.map(key => [ key, obj[key] ]);
}; |
Found a rather trivial way around this. Just just infer the type and then recast it. const renderedQuestions = Object.entries(questions)
.map(([questionId, questionValue]: [ID, *]) => {
const question: QuestionT = (questionValue: any);
// everything works as expected from here on out
return ();
}); |
This is what we're doing about this (for now):
|
@samwgoldman a year on, any update on better types for I don't know if this works (I'm not sure the precedence on overloaded definitions), but could we not leave the existing definition in place, and just put:
into the libdef as well? |
If anyone's looking for a good alternative to the native function with better typing, I'm using this monstrosity that works pretty well opaque type Unassignable = mixed
/**
* Like the builtin Object.entries function, but uses a typed object
* as the return values instead of tuples to get Flow working with it better.
*/
export function objectEntries<+T: any>(
obj: T
): $NonMaybeType<$Values<
$ObjMapi<T,
<Key, Val>(Key, Val) => {
+key: Key,
+val: Val,
// This is used to prevent accidental indexer access, e.g. entry[0]
-[mixed]: Unassignable,
}>
>>[] {
return Object.entries(obj).map(toObjectEntry)
}
function toObjectEntry([key, val]: [string, any]) {
return {key, val}
}
for (const entry of objectEntries({a: 'a', b: 2})) {
if (entry.key === 'a') {
entry.val.toUpperCase() // entry.val is inferred to be string here
} else {
entry.val.toFixed(2) // and number here
entry.val.foo // Error as expected
}
} This is still unsound with inexact objects, but we aren't using them much anyway so 🤷 |
Considering the number of issue linked, this seems to be a recurring pain point. Is there a plan to address this? |
It's a known issue and something we would like to address at some point. However, we have limited resources and other projects have priority at the moment. |
With above commit, object types that have an indexer and no explicit properties (e.g. |
Super cool! Thanks @gkz ! |
What we have today:
What may be better in the special case where
object
is used strictly as a map:(Note: I'm being deliberately cautious, which is also why this is not a PR 😄)
I tried out these declarations locally over the library ones, and while I was worried about breaking something in the heterogenous case (objects like
{a: 1, b: 'b'}
), Flow actually seemed to infer a validT
from whatever I threw at it, and carried on splendidly.So, unless I'm missing something, I think these can become the new declarations of
values
andentries
.The text was updated successfully, but these errors were encountered: