apollographql / apollographql/apollo-tooling
[Flow] Export each possible variant of a union as a separate type
- Dominant language
- TypeScript
- Stars
- 3k
- Forks
- 460
- PR merge metrics
- No merged PRs in 30d
Description
When I generate flow types for the following query:
query.graphql
```graphql
query contentQuery {
helpDocs_Content {
__typename
... on HelpDocs_Faq {
id
question
answer
}
... on HelpDocs_Tutorial {
id
title
introduction
conclusion
}
}
}
```
Based on the following schema:
schema.graphql
```graphql
type HelpDocs_Faq {
id: ID!
question: String!
answer: String!
}
type HelpDocs_Tutorial {
id: ID!
title: String!
introduction: String!
conclusion: String!
}
union HelpDocs_Content =
HelpDocs_Faq
| HelpDocs_Tutorial
type Query {
helpDocs_Content(slug: String!): HelpDocs_Content
}
```
I get the following types:
types.js
```javascript
export type contentQuery_helpDocs_Content = {
__typename: "HelpDocs_Faq",
id: string,
question: string,
answer: string,
} | {
__typename: "HelpDocs_Tutorial",
id: string,
title: string,
introduction: string,
conclusion: string,
};
export type contentQuery = {
helpDocs_Content: ?contentQuery_helpDocs_Content
};
```
In my code, I can use `__typename` to tell which variant I got and as long as I do that before using a field that is not found in all types, Flow will accept that:
```javascript
if (obj.__typename === 'HelpDocs_Faq') {
console.log(obj.question);
}
```
However, if I pass the data down to another function I'd still need to test for the `__typename` there as well:
```javascript
function handleFaq(obj: contentQuery_helpDocs_Content) {
// $FlowFixMe
console.log(obj.question);
}
if (obj.__typename === 'HelpDocs_Faq') {
handleFaq(obj);
}
```
What I'd like for codegen to generate instead is:
```javascript
export type contentQuery_helpDocs_Content_HelpDocs_Faq = {
__typename: "HelpDocs_Faq",
id: string,
question: string,
answer: any,
};
export type contentQuery_helpDocs_Content_HelpDocs_Tutorial = {
__typename: "HelpDocs_Tutorial",
id: string,
title: string,
introduction: any,
conclusion: any,
};
export type contentQuery_helpDocs_Content =
contentQuery_helpDocs_Content_HelpDocs_Faq
| contentQuery_helpDocs_Content_HelpDocs_Tutorial;
export type contentQuery = {
helpDocs_Content: ?contentQuery_helpDocs_Content
};
```
That way, I can use the variant types in the function without further checks, as Flow can see that what I pass to the function is the type it expects and within the function, it can rely on getting that type only.
```javascript
function handleFaq(obj: contentQuery_helpDocs_Content_HelpDocs_Faq) {
console.log(obj.question);
}
if (obj.__typename === 'HelpDocs_Faq') {
handleFaq(obj);
}
```
Contributor guide
Research direction
Reproduce the request using the query.graphql and schema.graphql examples, then inspect the code-generation entry point that produces types.js and locate its union-type tests. Done means generated output exports a distinct named type for each union variant while retaining the union alias and allowing the shown Flow narrowing and function call.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- graphql, typescript
- Domain
- developer-experience, tooling
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100