Flow refine a union type between arguments and return value of a function
- Dominant language
- Rust
- Stars
- 22.3k
- Forks
- 1.9k
- PR merge metrics
- No merged PRs in 30d
Description
## Proposal
I'm trying to figure out a way to represent basically a single disjoint union type that is split between function arguments and the return value. I don't think this is currently possible in flow, but it would be really useful.
## Use case
This could be useful in a lot of situations, but one specific example would be typing an API client. You have a single function, something like `client.request` and depending on the path and method of the request, the data returned will be a different shape.
Here's a code example of this. The interesting thing is, you can represent this using a disjoint union if you expose a callback API, but I don't think there's a way to represent it where the function returns the result.
```
type User = {id: number, name: string}
type Post = {id: number, content: string}
type APIRoute = {| +path: "/users", +method: "GET", +callback: (results: User[]) => void |} |
{| +path: "/posts", +method: "GET", +callback: (results: Post[]) => void |};
// Callback version works as expected
function apiClientCb(params: APIRoute): void {
// access api, call params.callback(results)
}
apiClientCb({path: "/users", method: "GET", callback: (results) => {
console.log("Fetched users", results.map(r => r.name)); // Works
console.log("Fetched posts", results.map(r => r.content)); // Error, as expected
}});
// Returning a value instead doesn't work
function apiClient(params) {
const p = new Promise(resolve => (
apiClientCb({...params, callback: (results) => resolve(results)})
));
return p;
}
apiClient({path: "/users", method: "GET"}).then(results => console.log("Fetched users", results.map(u => u.name)));
```
[TryFlow link](https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVAXAngBwKZgCqAzngE5gC8YA3gJYAmAXGAHYCuAtgEbkA0bAIac8LYhjJ1WAcwC+mXAQAKccVVqMWHHvzABjOKwx4jYiVLnps+MAEElASQBKcdsfU0APmADUOQRgAFiwARMDspGTEIQI+IkFwzGAhAOIAogAqMb56gjAw3IJ6ANYsABRkeMTsMBjELCTkANoAugCUVAB8YABucIxgnrKDqGBj4xOTtN5+AcHJwDiqddlxeAlJqZmrufmFJeWV1bX1YCrirR2U3X0DQwDc6FDsrHoYdIZggjh0AMIwdBMGF+3DK-jIwlO9mcrmMbRYtwYtFGYGAwC+ej0VWIXx+Al2MDA4MhADoCftihUqjU6m1UPJUN8-gCgSCyjR-EFQuFItEBPFAolQuksvi8gUiqUwFTjrSusixgZWMQ4DA8CT4NIyiEAGLrPSBPBIiLkPlgI404gkzjfCrysgk1jCPBtNr3VHogDqcDIxWIKLRSpVao1cC1uv1hqRS3EZotJ2ttoo13NZMMxiMrvdHrAaTIZB9AkEOLwAA98G8jfTZG70Khnq93p8mf9AUYwYIIZxiB0aCigxgiepWHgkEoC5w6KQZaqegQU2UUWMWyyjGyaCTN8Tu2K9pLDtSTldukdZ3gZZa2jWUVmUZUMOwyKwiY8GSu2xh2Zz5mETVFsgKQrJCKIQ1iSQQmBeJzykGqrqpq2p6hgBpGmAf5xoedSJjgZTsPK7COs6rpukAA)
I'm not even sure if the syntax exists to type the non-callback version. In the above code when trying to infer the type flow gives an error "Could not decide which case to select. Since case 1 [1] may work but if it doesn't case 2 [2] looks promising too."
A similar issue is: https://github.com/facebook/flow/issues/5675 but it is expressed a little bit differently and it hasn't gotten any responses.
Contributor guide
Assessment
This issue has not been assessed yet.