-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement the type for the compose() function natively
Summary: `compose()` is a utility used a fair deal in the React community. Especially for projects that make heavy use of HOCs. The `compose()` function is exported by [Redux](http://redux.js.org/docs/api/compose.html), [Recompose](https://github.com/acdlite/recompose/blob/master/docs/API.md#compose), Lodash (as [`flow`](https://lodash.com/docs/4.17.4#flow) and [`flowRight`](https://lodash.com/docs/4.17.4#flowRight)), and [Ramda](http://ramdajs.com/docs/#compose). All popular libraries in the JavaScript community. Currently compose is typed with [multiple predicates](https://github.com/flowtype/flow-typed/blob/aff2bf770eeb3791aba0f231616a1872d0ffbcf5/definitions/npm/redux_v3.x.x/flow_v0.33.x-/redux_v3.x.x.js#L55-L104) or an [intersection](https://github.com/flowtype/flow-typed/blob/aff2bf770eeb3791aba0f231616a1872d0ffbcf5/definitions/npm/recompose_v0.24.x/flow_v0.47.x-v0.52.x/recompose_v0.24.x.js#L71-L127). This approach has multiple downsides. You may only support composing up to `n` functions, you cannot use spreads on arrays of unknown arity, and when using an intersection it is easy [to fall into a bad error message](https://github.com/istarkov/flow-compose-error). (The intersection error message may be a bug and should probably be improved anyway at some point, but I think the case for implementing `compose()` natively is stronger then just improving the error message.) [istarkov](https://github.com/istarkov) also promised me a blog post if I implemented the type for compose natively. Reviewed By: avikchaudhuri Differential Revision: D5666855 fbshipit-source-id: 2d94c47df1d722933e21b0a693b0547958fe9efc
- Loading branch information
1 parent
d2b8633
commit ab9bf44
Showing
12 changed files
with
316 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[libs] | ||
lib/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// @flow | ||
|
||
declare var compose: $Compose; | ||
declare var composeReverse: $ComposeReverse; | ||
|
||
(compose(n => n.toString())(42): empty); // Error: string ~> empty | ||
|
||
(composeReverse(n => n.toString())(42): empty); // Error: string ~> empty | ||
|
||
(compose( | ||
n => n * 5, // Error: string cannot be multiplied. | ||
n => n.toString(), | ||
)(42): empty); // Error: number ~> empty | ||
|
||
(composeReverse( | ||
n => n * 5, // OK | ||
n => n.toString(), | ||
)(42): empty); // Error: string ~> empty |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
Error: basic.js:6 | ||
6: (compose(n => n.toString())(42): empty); // Error: string ~> empty | ||
^ number. Could not resolve name | ||
|
||
Error: basic.js:8 | ||
8: (composeReverse(n => n.toString())(42): empty); // Error: string ~> empty | ||
^ number. Could not resolve name | ||
|
||
Error: basic.js:10 | ||
v------- | ||
10: (compose( | ||
11: n => n * 5, // Error: string cannot be multiplied. | ||
12: n => n.toString(), | ||
13: )(42): empty); // Error: number ~> empty | ||
----^ number. This type is incompatible with | ||
13: )(42): empty); // Error: number ~> empty | ||
^^^^^ empty | ||
|
||
Error: basic.js:12 | ||
12: n => n.toString(), | ||
^ number. Could not resolve name | ||
|
||
Error: basic.js:17 | ||
17: n => n.toString(), | ||
^ number. Could not resolve name | ||
|
||
Error: recompose.js:23 | ||
23: c: Math.round(props.p), // Error: string ~> number | ||
^^^^ identifier `Math`. Could not resolve name | ||
|
||
Error: spread.js:8 | ||
v------- | ||
8: (compose( | ||
9: ...fns1, | ||
10: )(42): empty); // Error: number ~> empty | ||
----^ number. This type is incompatible with | ||
10: )(42): empty); // Error: number ~> empty | ||
^^^^^ empty | ||
|
||
Error: spread.js:12 | ||
v------- | ||
12: (compose( | ||
13: ...fns1, // Error: string ~> number | ||
14: )('foo'): empty); // Error: string ~> empty and number ~> empty | ||
-------^ number. This type is incompatible with | ||
14: )('foo'): empty); // Error: string ~> empty and number ~> empty | ||
^^^^^ empty | ||
|
||
Error: spread.js:12 | ||
v------- | ||
12: (compose( | ||
13: ...fns1, // Error: string ~> number | ||
14: )('foo'): empty); // Error: string ~> empty and number ~> empty | ||
-------^ string. This type is incompatible with | ||
14: )('foo'): empty); // Error: string ~> empty and number ~> empty | ||
^^^^^ empty | ||
|
||
Error: spread.js:13 | ||
13: ...fns1, // Error: string ~> number | ||
^^^^ string. This type is incompatible with the expected param type of | ||
4: declare var fns1: Array<(number) => number>; | ||
^^^^^^ number | ||
|
||
Error: spread.js:16 | ||
v------- | ||
16: (compose( | ||
17: ...fns2, // Error: string ~> number | ||
18: )(42): empty); // Error: number ~> empty and string ~> empty | ||
----^ number. This type is incompatible with | ||
18: )(42): empty); // Error: number ~> empty and string ~> empty | ||
^^^^^ empty | ||
|
||
Error: spread.js:16 | ||
v------- | ||
16: (compose( | ||
17: ...fns2, // Error: string ~> number | ||
18: )(42): empty); // Error: number ~> empty and string ~> empty | ||
----^ string. This type is incompatible with | ||
18: )(42): empty); // Error: number ~> empty and string ~> empty | ||
^^^^^ empty | ||
|
||
Error: spread.js:17 | ||
17: ...fns2, // Error: string ~> number | ||
^^^^ array | ||
17: ...fns2, // Error: string ~> number | ||
^^^^ string. This type is incompatible with the expected param type of | ||
5: declare var fns2: Array<(number) => string>; | ||
^^^^^^ number | ||
|
||
Error: spread.js:21 | ||
v------- | ||
21: (compose( | ||
22: ...fns3, // Error: Cannot get p on number | ||
23: )(x1): empty); // Error: number ~> empty and object ~> empty | ||
----^ number. This type is incompatible with | ||
23: )(x1): empty); // Error: number ~> empty and object ~> empty | ||
^^^^^ empty | ||
|
||
Error: spread.js:21 | ||
v------- | ||
21: (compose( | ||
22: ...fns3, // Error: Cannot get p on number | ||
23: )(x1): empty); // Error: number ~> empty and object ~> empty | ||
----^ object literal. This type is incompatible with | ||
23: )(x1): empty); // Error: number ~> empty and object ~> empty | ||
^^^^^ empty | ||
|
||
Error: spread.js:22 | ||
22: ...fns3, // Error: Cannot get p on number | ||
^^^^ number. Could not resolve name | ||
|
||
Error: spread.js:27 | ||
v-------- | ||
27: ((compose( | ||
28: ...fns3, | ||
29: ))(x2): empty); // Error: object ~> empty | ||
-----^ object type. This type is incompatible with | ||
29: ))(x2): empty); // Error: object ~> empty | ||
^^^^^ empty | ||
|
||
|
||
Found 17 errors |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
declare module 'recompose' { | ||
declare export var compose: $Compose; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/** | ||
* @flow | ||
* | ||
* This test was taken from: | ||
* https://github.com/istarkov/flow-compose-error | ||
*/ | ||
|
||
import { compose } from 'recompose'; | ||
|
||
// shared code between bad/good Compose | ||
type Comp<A> = (a: A) => void; | ||
type HOC<A, B> = (a: Comp<A>) => Comp<B>; | ||
|
||
function myEnhancer<A, B>(mapper: B => A): HOC<A, B> { | ||
return (comp: Comp<A>) => (props: B) => comp(mapper(props)); | ||
} | ||
|
||
const enhancer: HOC<*, { p: number, e: string }> = compose( | ||
myEnhancer(props => ({ | ||
p: `${props.p * 3}`, | ||
})), | ||
myEnhancer(props => ({ | ||
c: Math.round(props.p), // Error: string ~> number | ||
})) | ||
); |
Oops, something went wrong.
ab9bf44
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.
thank you thank you thank you!
ab9bf44
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 wasn't the documentation added in this commit?
ab9bf44
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.
Or any subsequent commit, for that matter?