Type-safe updaters: non-assignability of unions
- Dominant language
- Rust
- Stars
- 19k
- Forks
- 1.9k
- PR merge metrics
- No merged PRs in 30d
Description
I seem to have encountered an issue with the new typesafe updaters. Here's the basic repro: https://github.com/Malien/typesafe-relay-updaters-repro
_Obligatory "using typescript, not flow" mention_
Assume we have a simple Todo app with todo groupings. Like this:
- ~~Make coffee~~
- Touch grass
- Buy groceries
- Tomatoes
- ~~Bread~~
- Work out
Let us have the following gql schema:
```graphql
type Query {
todos: [Todo!]!
}
interface Node {
id: ID!
}
union Todo = TodoItem | TodoGroup
type TodoItem implements Node {
id: ID!
title: String!
completed: Boolean!
}
type TodoGroup implements Node {
id: ID!
title: String!
todos: [TodoItem!]!
}
type Mutation {
createTodo(title: String!): TodoItem!
}
```
```graphql
fragment TodoList_assignable_todo on Todo @assignable {
__typename
}
```
given the query:
```graphql
query TodoListQuery {
todos {
...Todo_todo # Some component that renders the todo items/groups
...TodoList_assignable_todo
}
}
```
and the mutation
```graphql
mutation TodoListAddTodoMutation($title: String!) {
createTodo(title: $title) {
...Todo_todo
...TodoList_assignable_todo
}
}
```
the update of
```js
function updater(store, response) {
const newTodo = response?.createTodo;
if (!newTodo) return;
const { updatableData } = store.readUpdatableQuery(
graphql`
query TodoListUpdateQuery @updatable {
todos {
...TodoList_assignable_todo
}
}
`,
{},
);
updatableData.todos = [...todos, newTodo];
}
```
fails to typecheck, since the generated types from `TodoListAddTodoMutation` is:
```ts
export type TodoListAddTodoMutation$data = {
readonly createTodo: {
readonly __id: string;
readonly __isTodoList_assignable_todo?: "TodoItem";
readonly " $fragmentSpreads": FragmentRefs<"TodoList_assignable_todo" | "Todo_todo">;
};
};
```
`__isTodoList_assignable_todo` is optional.
This seems to be related to the sections of the docs ["where is guaranteed to implement an interface"](https://relay.dev/docs/guided-tour/updating-data/imperatively-modifying-linked-fields/#example-assigning-to-an-interface-when-the-source-is-guaranteed-to-implement-that-interface)/["where is not guaranteed to implement an interface"](https://relay.dev/docs/guided-tour/updating-data/imperatively-modifying-linked-fields/#example-assigning-to-an-interface-when-the-source-is-not-guaranteed-to-implement-that-interface), yet this seems like a missed case.
`TodoItem` (the return type of `createTodo`) is guaranteed to """implement""" union `Todo`. Yet this requires additional type-guard function (aka. validators). In the end the new typesafe updaters code is a lot more cumbersome to deal with, in comparison to the old unsafe variant. Not to mention all of the new concepts of assignability and updatability being thrown back at you.
Contributor guide
Assessment
This issue has not been assessed yet.