-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Fix flowtypes for OrderedSet, OrderedMap, toOrderedSet, and toOrderedMap #1027
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
Conversation
Unfortunately extending Set and Map caused issues with OrderedSet and OrderedMap flow annotations. For example, ``` const orderedSet: OrderedSet<number> = OrderedSet.of(1,2,3); ``` was not valid, because `of` was incorrectly assumed to return a `Set<number>` rather than an `OrderedSet<number>`. The fix was to copy and paste the Set and Map flow annotations, adjusting for OrderedSet and OrderedMap as appropriate. See immutable-js#1015 for more details.
|
If there's a way to fix this without copy pasting flow annotations for Map / Set, I'm happy to update this PR as necessary. I made this PR because with immutable 3.8.1 I couldn't use |
|
|
||
| // OrderedMaps have nothing that Maps do not have. We do not need to override constructor & other statics | ||
| declare class OrderedMap<K,V> extends Map<K,V> { | ||
| declare class OrderedMap<K,V> extends KeyedCollection<K,V> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you decide to extend KeyedCollection? OrderedMap extends Map.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Turns out I'm getting an issue overriding update by extending Map.
I'll leave it as extending KeyedCollection for the purposes of this PR. Having OrderedSet extend Set worked without issues (I didn't realize I was already doing that), and I'll update the PR to remove extraneous OrderedSet definitions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's because of this in the update definition. Does it happen if you replace this on line 574 with OrderedMap<K,V> and with `Map<K,V> on line 580?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately the updater function is still problematic. I can work around it by changing the update signature from
update<K_,V_>(updater: (value: Map<K,V>) => Map<K_,V_>): Map<K_,V_>;
to
update<K_,V_>(updater: (value: any) => any): Map<K_,V_>;
but I'm not sure how else to fix it. The error being generated is as follows:
inconsistent use of library definitions
571: update<K_,V_>(updater: (value: OrderedMap<K,V>) => OrderedMap<K_,V_>): OrderedMap<K_,V_>;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ intersection. This type is incompatible with
500: update<K_,V_>(updater: (value: Map<K,V>) => Map<K_,V_>): Map<K_,V_>;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ polymorphic type: function type
Member 1:
571: update<K_,V_>(updater: (value: OrderedMap<K,V>) => OrderedMap<K_,V_>): OrderedMap<K_,V_>;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ polymorphic type: function type
Error:
inconsistent use of library definitions
500: update<K_,V_>(updater: (value: Map<K,V>) => Map<K_,V_>): Map<K_,V_>;
^^^^^^^^^^ Map. This type is incompatible with
571: update<K_,V_>(updater: (value: OrderedMap<K,V>) => OrderedMap<K_,V_>): OrderedMap<K_,V_>;
^^^^^^^^^^^^^^^^^ OrderedMap
Member 2:
572: update<V_>(key: K, updater: (value: V) => V_): OrderedMap<K,V|V_>;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ polymorphic type: function type
Error:
inconsistent use of library definitions
500: update<K_,V_>(updater: (value: Map<K,V>) => Map<K_,V_>): Map<K_,V_>;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ undefined (too few arguments, expected default/rest parameters). This type is incompatible with
572: update<V_>(key: K, updater: (value: V) => V_): OrderedMap<K,V|V_>;
^^^^^^^^^^^^^^^^ function type
Member 3:
573: update<V_>(key: K, notSetValue: V_, updater: (value: V) => V_): OrderedMap<K,V|V_>;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ polymorphic type: function type
Error:
inconsistent use of library definitions
500: update<K_,V_>(updater: (value: Map<K,V>) => Map<K_,V_>): Map<K_,V_>;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ undefined (too few arguments, expected default/rest parameters). This type is incompatible with
573: update<V_>(key: K, notSetValue: V_, updater: (value: V) => V_): OrderedMap<K,V|V_>;
^^^^^^^^^^^^^^^^ function type
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extending KeyedCollection is better IMO.
| orderedStringToNumber = OrderedMap({'a': 1}).mapKeys(x => x) | ||
| orderedStringToNumber = OrderedMap({'a': 1}).flatten() | ||
| orderedStringToNumber = OrderedMap({'a': 1}).flatten(1) | ||
| orderedStringToNumber = OrderedMap({'a': 1}).flatten(true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the tests! It would be great if there were tests for expected errors, too. You can do it with // $ExpectError. See here.
|
Good job. I think it's good to merge when there are tests for false positives ( |
|
I'm currently working on false positives, will update this PR tonight or tomorrow. I've found a few edge cases that I'll add TODOs / FIXMEs with explanations. |
One thing that came up as a result of this is that it seems like the current flow definitions are laxer than typescript definitions. For example, typescript's intersect looks like ``` intersect(...iterables: Array<T>[]): Set<T> ``` whereas flow's intersect looks like ``` intersect<U>(...iterables: ESIterable<U>[]): Set<T&U> ``` Also removed unnecessary OrderedSet overrides such as * delete * remove * clear since we were already extending Set. Also removed unused variables in immutable-flow tests.
|
Updated PR with negative test cases, and removed some unnecessary variables / overrides. |
|
The flow definitions seems to be a little laxer than the typescript definitions, I'm not sure if this is by design, so I left it alone. |
|
Out of curiosity - what do you exactly mean by laxer? They are verbose, it's true. I don't have any experience with TS so I cannot compare. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the tests. They are very detailed. I love that. I think that it would be great if the issues with flow definitions landed in a collective issue. Maybe some of them are more controversial so they probably deserve their own issue.
Asking @lacker for an opinion since I'm just helping here
type-definitions/immutable.js.flow
Outdated
| static isOrderedSet(maybeOrderedSet: any): bool; | ||
|
|
||
| add<U>(value: U): OrderedSet<T|U>; | ||
| delete(value: T): this; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice!
| // Some tests look like they are repeated in order to avoid false positives. | ||
| // Flow might not complain about an instance of (what it thinks is) T to be assigned to T<K, V> | ||
|
|
||
| import Immutable, { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it deleted?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Immutable wasn't actually being used, so I got rid of it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It tested valid imports. I know it might be confusing though. I should've put a comment there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting -- can you elaborate on that? I'll switch back to
import Immutable, {
// ...
} from 'immutable';but I'm not clear as to what importingImmutable is testing. Should I add back
import * as Immutable2 from 'immutable'
const ImmutableList = Immutable.List
const ImmutableMap = Immutable.Map
const ImmutableStack = Immutable.Stack
const ImmutableSet = Immutable.Set
const ImmutableKeyedIterable = Immutable.KeyedIterable
const ImmutableRange = Immutable.Range
const ImmutableRepeat = Immutable.Repeat
const ImmutableIndexedSeq = Immutable.IndexedSeq
const Immutable2List = Immutable2.List
const Immutable2Map = Immutable2.Map
const Immutable2Stack = Immutable2.Stack
const Immutable2Set = Immutable2.Set
const Immutable2KeyedIterable = Immutable2.KeyedIterable
const Immutable2Range = Immutable2.Range
const Immutable2Repeat = Immutable2.Repeat
const Immutable2IndexedSeq = Immutable2.IndexedSeqas well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made it - I know it's confusing. It tests if (1) the default import (2) the import * as X from 'immutable' (3) individual imports are supported.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, I'll add that back and include a comment as to what it's testing. Thanks for the clarification.
| // $ExpectError | ||
| stringToNumber = Map([['a', 'b']]) | ||
| // FIXME: this should trigger an error -- this is actually a Map<string, string> | ||
| stringToNumber = Map(List.of(List(['a', 'b']))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great find! Would you mind adding an issue for it? FIXMEs get lost pretty easily.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I'll create an issue with a list of flowtype wonkiness I've found.
| * ...iterables: Iterable<K, V>[] | ||
| * ``` | ||
| */ | ||
| stringToNumber = Map({'a': 1}).mergeWith((previous, next, key) => 1, [1]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand why it should throw an error :(.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah! Gotcha.
| * This seems inconsistent with the typescript signature of | ||
| * | ||
| * ``` | ||
| * update(updater: (value: Map<K, V>) => Map<K, V>): Map<K, V> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, great find again. Would you mind adding an issue (again)?
type-definitions/immutable.js.flow
Outdated
| ): OrderedMap<K|K_,V|V_>; | ||
|
|
||
| mergeWith<K_,W,X>( | ||
| merger: (previous: V, next: W, key: number) => X, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't key be of type K here? And K_ in the output?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that makes sense to me -- this was copy pasted from
2363
Map so this issue exists there as well. The other thing to note is that iterables should probably be ESIterable<K_,W> rather than ESIterable<W>[].
|
To clarify on
I meant that methods that make persistent changes to the data structure in flow are allowed to introduce new types, whereas they are not allowed to introduce new types in typescript. For example, this is the and this is the So we can do the following in flow but not in typescript. This behavior is also allowed in javascript for what it's worth. This seems like an inconsistency that I thought I'd bring up, and probably belongs in a separate issue. |
|
I made a gigantic issue here: #1029, let me know if you want me to split it up into mini issues. |
|
Great job. The only remaining issue are the |
|
The |
There were some issues with the definitions of Map and OrderedMap's flowtype signatures for mergeWith and mergeDeepWith. 1. the merger function's `key` was incorrectly specified as being of type number 2. ...iterables was incorrectly specified to be array like rather than object like
|
Updated with commits to add back the Import tests, and fix mergeWith and mergeDeepWith type signatures for Maps and OrderedMaps. I'm currently working on fixing |
The `updater` argument was specified as `any` rather than ``` (value: any) => any ```
|
Updated with fix to |
|
@lacker I think it's good to merge. |



Unfortunately extending Set and Map caused issues with OrderedSet
and OrderedMap flow annotations. For example,
was not valid, because
ofwas incorrectly assumed to return aSet<number>rather than anOrderedSet<number>.The fix was to copy and paste the Set and Map flow annotations,
adjusting for OrderedSet and OrderedMap as appropriate.
See #1015 for more details.
Add negative flow test cases for OrderedSet and OrderedMap
One thing that came up as a result of this is that it seems
like the current flow definitions are laxer than typescript
definitions. For example, typescript's intersect looks like
whereas flow's intersect looks like
Also removed unnecessary OrderedSet overrides such as
since we were already extending Set.
Also removed unused variables in immutable-flow tests.