Incorrect mutation caller return type — `data` is nullish when `errorPolicy` is `none`

Consider the following example:

  const [createPost] = useMutation(CreatePostDocument);

  const handleSubmit = async () => {
    const { data } = await createPost({ variables: { text: 'abc' } });
    if (data != null) {
      navigateToPost({ id: data.post.id });
    }
  };

The problem is: because the errorPolicy is none (the default), the data != null condition is always true. So:

  • I cannot achieve 100% test coverage, because it’s impossible to write a correct test case where the condition would fail.
  • Type assertions like ! are not allowed in my project because of generally being a bad practice and a common cause of bugs.

I’m wondering what’s the recommended solution here, or should this be considered a bug or a missing feature.

Thanks!

Typing things like errorPolicy in these situations unfortunately makes things a lot more complex - especially in “indirect” situations like here.

We do that in a few places in Apollo Client, but not in useMutation.

Since the types here are technically correct (a function being declared as returning A | B always returning A is 100% correct in TypeScript), I don’t think that this will change in the near future in Apollo Client.

I think you’ll need to get to a point where you’ll have to either lower your test coverage requirement or allow for type assertions under specific circumstances. Type assertions are meant for situations where you as a programmer know better than the compiler, and here that is the case.

You could also use an assertion function here:

function assertNonNull<T>(val: T | null): asserts val is T {
  if (val === null) throw new Error("This was unexpected");
}

  const handleSubmit = async () => {
    const { data } = await createPost({ variables: { text: 'abc' } });
    assertNonNull(data)
    navigateToPost({ id: data.post.id });
  };
1 Like

Thanks! This sounds very reasonable, I just wanted to be sure I wasn’t missing anything.