Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type-safe updaters: non-assignability of unions #4560

Open
Malien opened this issue Dec 29, 2023 · 1 comment · May be fixed by #4603
Open

Type-safe updaters: non-assignability of unions #4560

Malien opened this issue Dec 29, 2023 · 1 comment · May be fixed by #4603

Comments

@Malien
Copy link

Malien commented Dec 29, 2023

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:

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!
}
fragment TodoList_assignable_todo on Todo @assignable {
  __typename
}

given the query:

query TodoListQuery {
  todos {
    ...Todo_todo # Some component that renders the todo items/groups
    ...TodoList_assignable_todo
  }
}

and the mutation

mutation TodoListAddTodoMutation($title: String!) {
  createTodo(title: $title) {
    ...Todo_todo
    ...TodoList_assignable_todo
  }
}

the update of

function updater(store, response) {
  const newTodo = response?.createTodo;
  if (!newTodo) return;

  const { updatableData } = store.readUpdatableQuery<TodoListUpdateQuery>(
    graphql`
      query TodoListUpdateQuery @updatable {
        todos {
          ...TodoList_assignable_todo
        }
      }
    `,
    {},
  );

  updatableData.todos = [...todos, newTodo];
}

fails to typecheck, since the generated types from TodoListAddTodoMutation is:

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"/"where is not guaranteed to implement an 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.

@Malien Malien changed the title Type-safe updaters non-assignability of unions Type-safe updaters: non-assignability of unions Dec 29, 2023
@Malien
Copy link
Author

Malien commented Dec 30, 2023

I'm exploring possibilities of opening a PR. Would need some time to familiarise myself with the compiler. Luckily compilers and Rust are not foreign to me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant