Skip to content

Typescript - useMutation variables can't handle unions #710

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

Closed
bviebahn opened this issue Jul 7, 2020 · 4 comments
Closed

Typescript - useMutation variables can't handle unions #710

bviebahn opened this issue Jul 7, 2020 · 4 comments

Comments

@bviebahn
Copy link
Contributor

bviebahn commented Jul 7, 2020

async function mutationFn(variables: { a: string } | { b: string }) {
    return true;
}

const useFoo = () => {
    const [mutate] = useMutation(mutationFn);
    mutate({ a: "" });
};

The code above produces this Typescript error:
Argument of type '{ a: string; }' is not assignable to parameter of type '{ a: string; } & { b: string; }'. Property 'b' is missing in type '{ a: string; }' but required in type '{ b: string; }'.

I have no idea why the expected type becomes the intersection { a: string; } & { b: string; } rather than the union I specified.

The type definition for MutateFunction is:

export type MutateFunction<
  TResult,
  TVariables,
  TError = Error
> = TVariables extends undefined
  ? (options?: MutateOptions<TResult, TVariables, TError>) => Promise<TResult>
  : (
      variables: TVariables,
      options?: MutateOptions<TResult, TVariables, TError>
    ) => Promise<TResult>

changing TVariables extends undefined to [TVariables] extends [undefined] seems to fix the problem. I don't understand why though.

I'm using version 2.4.13

@serhii-ohorodnyk
Copy link

I bumped into the same issue. And it is a feature of typescript which is called Distributive conditional types.

With current implementation it means that for mutation function with string | number we will get union type of function (string) => Promise<TResult> | (number) => Promise<TResult> which will fail compilation. Expected type should be (string | number) => Promise<TResult>.

And wrapping conditional type into 1-tuple (convert TVariables extends undefined to [TVariables] extends [undefined] as you mentioned @Bene8493) works because type is not naked anymore. And it is a common way to avoid type distribution.

I'd be happy to raise a PR with this change, but if I get contribution guide correctly I should be assigned to this issue first, or...?

@bviebahn
Copy link
Contributor Author

Thanks for looking into it. There's an open pull request to completely move the project to typescript. Might be worth waiting for that. #767

@MichaelDeBoey
Copy link
Collaborator

Is this still a valid problem now #767 is merged? 🤔

@boschni
Copy link
Collaborator

boschni commented Jul 30, 2020

Seems to be working correctly now when using the example in the React Query tests

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

No branches or pull requests

5 participants