facebook / facebook/flow

Flow refine a union type between arguments and return value of a function

Open
#7,695 1 comment 0 reactions 0 assignees View on GitHub
discussion
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.