-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
Fix typescript types for typescript 2.4 #1285
Fix typescript types for typescript 2.4 #1285
Conversation
`Keyed.flatMap` says that `mapper` is allowed to change the key type and the value type, but `Collection.keyMap` says that `mapper` is only allowed to change the value type. This commit resolves the typing problem by making the return type's type arguments both `any`. It does not resolve the mismatch in the implementation of Map.flatMap, for example.
M should actually be Iterable<M> since this is map, not flatMap.
The previous type, Set<T> extends Collection<T, T>, results in an incorrect type for `map` because `map` is not allowed to change the key type, only the value type. But map's previous return type of Set<T> asserted that both the key *and* the value type changed. Now the key type is always `never`.
Missed in previous commit
This works around the out-of-memory crash in Typescript 2.4 that arises from the compiler doing too much work when checking the correctness of `extends` relationships. The Typescript compiler got caught in a deep recursion when checking `toSeq: Seq<K, V>`, the previous type.
Thank you for your pull request and welcome to our community. We require contributors to sign our Contributor License Agreement, and we don't seem to have you on file. In order for us to review and merge your code, please sign up at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need the corporate CLA signed. If you have received this in error or have any questions, please contact us at cla@fb.com. Thanks! |
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Facebook open source project. Thanks! |
We noticed the issue with Set and map, flatMap as well. (See #879) |
Some immutable tooling doesn't understand never yet.
Looks like some doc-generation tooling doesn't understand |
Some immutable tooling doesn't understand void or never.
The failing tests points out an additional bug with the definition of I'm still working on running tests locally, but I don't know how to copy type-definitions/Immutable.d.ts to dist/immutable-nonambient.d.ts, which is what the tests refer to. I poked around the gulpfile and didn't find anything? Is this manual? |
Never mind, I eventually got it by running through the tasks in package.json |
Set.has shouldn't be used; it refers to the key type of the collection and the key type of Set is always number. Compare indexed types like List, where this is also true.
1. Set<T> once again extends Collection<never, T>. 2. pages/lib/TypeKind.js understands Never now. 3. Remove calls to Set.has and Set.get. These refer to the key type, which is now `never`. Set.contains should be used instead of Set.has. `if (Set.contains(x)) Set.get(x)` should be replaced with `if (Set.contains(x)) x` since Sets shouldn't change the identity of their items.
this could be totally the wrong place to report this (or maybe not?) but I tried to port my immutables‑using typescript app to See my commit: I would have thought the extra cast I added to make it build wouldn't be needed? Without it, it appears to infer type That cast was not needed with tsc 2.3 and the previous immutables TS bindings (tsc goes out of memory on this code with both 2.4.2 and 2.5RC). |
Previously it was any to work around Typescript 2.4's out-of-memory crash.
@emmanueltouzery the problem is that |
I tracked the problem back to the preceding declaration: const controllerNameToFilename =
Map(
viewInfos
.filter(vi => vi.controllerName.isSome())
// JS files are not going to have a scope interface
// definition so they're not helpful. Also, we can
// get twice the same file: original TS & compiled JS.
// => keep only the original TS in that case.
.filter(vi => vi.fileName.toLowerCase().endsWith(".ts"))
.map(vi => [vi.controllerName.some(), vi.fileName] as [string, string])); Notice that I added as [string, string] there at the end. Without it, the return type is inferred as |
Happy to see work here, but some feedback:
|
Here's an example of where code goes wrong without let nset = Immutable.Set.of(1,2,3,4)
let ncol: Immutable.Collection<number, number> = nset;
nset.map(v => v.toString()).map((v, k) => k.length);
ncol.map(v => v.toString()).map((v, k) => k.length); The last two lines produces With I understand this is a break for Typescript 2.3 code that uses the keys of a Set today, so if you have guidance I would appreciate it. I'm still thinking about the second case; there might be a way to avoid breaks there. |
@sandersn thanks for the example, I think perhaps this would be better solved by a more specific type for |
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.
.
OK, I restored the original behaviour for subclasses. I had to do this by adding overloads in the base classes (Seq, Collection, etc) to account for the inconsistent types coming from Set and Dictionary. Instead of a single more specific type, this allows overrides to override one of two different types. Most of these overloads are reasonable, except for |
Also, overloads are not inherited, so only uses of the base Collection interface will see the bogus overload. Most people will only ever see the methods that go with the collection type they're using. |
Squashed commit of the following: commit d87e7756aa0310136436452cb2e783b7b7134f39 Author: Travis CI <github@fb.com> Date: Fri Sep 29 18:47:46 2017 -0700 Revert changes to tests that seem unnecessary, update package.json typescript to 2.5 to properly run tests and prove no deadlock commit 55d6027ce92c761bceae825830678dec9c84fb95 Merge: 5381bfc 041cc87 Author: Travis CI <github@fb.com> Date: Fri Sep 29 18:12:58 2017 -0700 Merge branch 'fix-typescript-types-for-typescript-2.4' of https://github.com/sandersn/immutable-js into sandersn-fix-typescript-types-for-typescript-2.4 commit 041cc87 Author: Nathan Shively-Sanders <nathansa@microsoft.com> Date: Fri Sep 29 13:50:09 2017 -0700 Update dist and ts-tests commit c83e64c Merge: f393a68 37195e5 Author: Nathan Shively-Sanders <nathansa@microsoft.com> Date: Fri Sep 29 13:42:09 2017 -0700 Merge branch 'master' into fix-typescript-types-for-typescript-2.4 commit f393a68 Author: Nathan Shively-Sanders <nathansa@microsoft.com> Date: Fri Sep 29 13:40:54 2017 -0700 Update dist files commit 2e7576e Author: Nathan Shively-Sanders <nathansa@microsoft.com> Date: Fri Sep 29 13:39:37 2017 -0700 Overloads allow inconsistent Collection subclasses commit 482d419 Author: Nathan Shively-Sanders <nathansa@microsoft.com> Date: Tue Sep 5 11:19:45 2017 -0700 Return Collection.toSeq's origin type: Seq<K, V> Previously it was any to work around Typescript 2.4's out-of-memory crash. commit b9ae2bd Author: Nathan Shively-Sanders <nathansa@microsoft.com> Date: Wed Aug 9 13:09:57 2017 -0700 Change Set key type back to never 1. Set<T> once again extends Collection<never, T>. 2. pages/lib/TypeKind.js understands Never now. 3. Remove calls to Set.has and Set.get. These refer to the key type, which is now `never`. Set.contains should be used instead of Set.has. `if (Set.contains(x)) Set.get(x)` should be replaced with `if (Set.contains(x)) x` since Sets shouldn't change the identity of their items. commit ea7ffae Author: Nathan Shively-Sanders <nathansa@microsoft.com> Date: Wed Aug 9 12:52:26 2017 -0700 Update .d.ts files in dist/ commit af305ec Author: Nathan Shively-Sanders <nathansa@microsoft.com> Date: Wed Aug 9 12:49:48 2017 -0700 Tests:change Set.has->Set.contains Set.has shouldn't be used; it refers to the key type of the collection and the key type of Set is always number. Compare indexed types like List, where this is also true. commit adb9326 Author: Nathan Shively-Sanders <nathansa@microsoft.com> Date: Wed Aug 9 10:59:14 2017 -0700 Set<T> extends Collection<number, T>, not never Some immutable tooling doesn't understand void or never. commit f9c94f9 Author: Nathan Shively-Sanders <nathansa@microsoft.com> Date: Wed Aug 9 10:53:34 2017 -0700 Set<T> extends Collection<void, T>, not never Some immutable tooling doesn't understand never yet. commit 96b81ea Author: Nathan Shively-Sanders <nathansa@microsoft.com> Date: Wed Aug 9 09:56:49 2017 -0700 Work around TS2.4:Collection.toSeq now returns any This works around the out-of-memory crash in Typescript 2.4 that arises from the compiler doing too much work when checking the correctness of `extends` relationships. The Typescript compiler got caught in a deep recursion when checking `toSeq: Seq<K, V>`, the previous type. commit 9b0eb51 Author: Nathan Shively-Sanders <nathansa@microsoft.com> Date: Wed Aug 9 09:51:27 2017 -0700 Make OrderedSet's [flat]map/filter have key:never Missed in previous commit commit e7d7ec9 Author: Nathan Shively-Sanders <nathansa@microsoft.com> Date: Wed Aug 9 09:35:16 2017 -0700 Set types:K type parameter is now never The previous type, Set<T> extends Collection<T, T>, results in an incorrect type for `map` because `map` is not allowed to change the key type, only the value type. But map's previous return type of Set<T> asserted that both the key *and* the value type changed. Now the key type is always `never`. commit 60fc276 Author: Nathan Shively-Sanders <nathansa@microsoft.com> Date: Wed Aug 9 09:32:15 2017 -0700 Return Stack.flatMap's mapper's return type M should actually be Iterable<M> since this is map, not flatMap. commit c750da4 Author: Nathan Shively-Sanders <nathansa@microsoft.com> Date: Tue Aug 8 16:25:55 2017 -0700 Loosen type of Keyed.flatMap and descendants `Keyed.flatMap` says that `mapper` is allowed to change the key type and the value type, but `Collection.keyMap` says that `mapper` is only allowed to change the value type. This commit resolves the typing problem by making the return type's type arguments both `any`. It does not resolve the mismatch in the implementation of Map.flatMap, for example.
Thanks for your help on this. I amended a few changes in the process of merging this, please let me know if any follow ups are necessary |
…ings * facebook/master: (48 commits) 4.0.0-rc.3 Use latest immutable.js build on website Only build docs on tagged releases (immutable-js#1321) Relicense as MIT (immutable-js#1320) Fix rendering issue on tall screens Merge immutable-js#1285 Do not throw from hasIn (immutable-js#1319) Add more flow tests for Records Upgrade prettier and jest (immutable-js#1318) Merge immutable-js#1193 Minor flow record fixes Merges immutable-js#1195 fixed immutable-js#1290 by removing relative position from star count (immutable-js#1317) Fix deploy script Adds a script which automatically builds a #npm branch (immutable-js#1316) Use .github directory Merges immutable-js#1314 Flow config ignore all node_modules Update to latest version of flow (immutable-js#1312) fixed immutable-js#1313 by fixing two borked reducers (immutable-js#1315) ...
Nope, looks good. I forgot about the test changes for Set that no longer needed to be changed. |
Typescript does a lot more analysis than previous versions. This (1) exposed some bugs in the types and (2) caused the compiler to run out of memory and crash. The crash, and many of the new checks, can be avoided by passing
--noStrictGenericChecks
but users of immutable shouldn't be required to do that.Improved Checks
Stack.flatMap
'smapper
was incorrectlyM
but should beIterable<M>
.Set
and related types should extendCollection<never, T>
notCollection<T, T>
. That's becauseCollection.map
(and flatMap and filter) assert that the returned collection has the same key type and a different value type. ButSet.map
returnsSet<T>
, which, if it extendsCollection<T, T>
changes both the key type and value type. Changing toSet<T> extends Collection<never, T>
means that the key type never changes.Workarounds
Map
and related types have aflatMap
method that is not a validCollection.flatMap
implementation because it changes the key type and value type, much likeSet
(see flatMap typescript type for Keyed, [Ordered]Map are not substitutable for Collection.flatMap #1283). However, here the implementation and the type are wrong, so I worked around the problem by lettingMap.flatMap
returnMap<any, any>
instead ofMap<KM, VM>
(the previous type) orMap<K, M>
(a valid subtype of Collection.flatMap).extends
relationships betweenCollection
and its subtypes. For example,OrderedMap extends Map extends Collection.Keyed extends Collection
. The worst offender istoSeq
since it refers to a subtype, Seq, which is then extended multiple times by further subtypes. This makes checking goes around in circles multiple times for each subtype. I chose to rely on upcoming optimisations in Typescript 2.5.3 that will short-circuit most of these cycles.Both workarounds have multiple possible fixes; for example, workaround (1) could instead declare two overloads of
flatMap
, one with the Collection-compatible type and one with the current type. Workaround (2) could deletetoSeq
entirely fromCollection
, or change the return type of toSeq toany
inCollection
, or change the return type oftoSeq
in subtypes of Collection.