1

[edit] I've created a very simplified example of what my problem is: https://codesandbox.io/s/example-type-inference-for-generic-types-qyqz1?file=/src/index.ts

So I'm trying to make a wrapper that accepts the form-handler and mutation (generated with graphql-let), to return a function that can be used in a form.

This is the code, but it throws an error on the variables in mutation({ variables: {...}) (see below).

Type '{ input: UnpackNestedValue; }' is not assignable to type 'TVariables'. 'TVariables' could be instantiated with an arbitrary type which could be unrelated to '{ input: UnpackNestedValue; }'.ts(2322)
types.d.ts(92, 5): The expected type comes from property 'variables' which is declared here on type 'MutationFunctionOptions<TData, TVariables>'

I understand that TVariables and TData can be different, but TVariables has default of OperationVariables which is type { [key: string]: any }, which is pretty generic.

How to solve this?

import {
  FetchResult,
  MutationFunctionOptions,
  OperationVariables,
} from '@apollo/client';
import { UseFormHandleSubmit } from 'react-hook-form';

export const submitHandlerMutationFactory = <
  TForm,
  TData,
  TVariables = OperationVariables
>(
  submitHandler: UseFormHandleSubmit<TForm>,
  mutation: (
    options?: MutationFunctionOptions<TData, TVariables>
  ) => Promise<FetchResult<TData>>
) => {
  return submitHandler((data) => mutation({ variables: { input: data } }));
};

EDIT after Kiuhnm's answer

It doesn't give the error anymore, but now I'm also missing the goal: getting the function to make sure the Types of the submitHandler and the mutation are the same.

However, if I use this, we get problems with the input variable, similar to previous problem, but it DOES give me information over when calling the the function with two not-matching types.

export const submitHandlerMutationFactory = <TFormInputs, TMutation>(
  submitHandler: UseFormHandleSubmit<TFormInputs>,
  mutation: Apollo.MutationTuple<TMutation, { input: TFormInputs }>[0]
) => {
  return submitHandler((data) => mutation({ variables: { input: data } }));
  // Type 'UnpackNestedValue<TFormInputs>' is not assignable to type 'TFormInputs'.
  //   'TFormInputs' could be instantiated with an arbitrary type which could be
  //   unrelated to 'UnpackNestedValue<TFormInputs>'.ts(2322)
};

const {
  register,
  handleSubmit,
  watch,
  formState: { errors },
} = useForm<LoginFormInputs>();

const [loginMutation, { data, loading, error }] = useLoginMutation();

type LoginFormInputs = {
  username: string;
  SOMETHING: string; // see explicit error below
};

const formSubmitHandler = submitHandlerMutationFactory(
  handleSubmit,
  loginMutation
  // Property 'password' is missing in type 'LoginFormInputs' but required in type 'LoginInput'
);
Highmastdon
  • 6,960
  • 7
  • 40
  • 68

1 Answers1

0

f1 and f2 can't be composable in this way, because f2 return type is incompatible with f1 argument type. Not sure how you want to compose them.


function f1(param: { x: string; y: number }) {
  return { a: param.x, b: param.y };
}

function f2(param: { a: string; b: number }) {
  return param;
}

function wrapperFactory<T1, T2>(
  fn1: (param: T2) => T2,
  fn2: (param: T1) => T2
) {
  return (param: T1) => fn1(fn2(param));
}

// none of these called functions have to pass the generic type
// they still are being validated perfectly

// ERROR
wrapperFactory(f1, f2)({ x: "", y: 0 });

// ERROR
wrapperFactory(f1, f2)({ b: "", y: 0 });

// ERROR
wrapperFactory(
  (param: { g: number; h: string }) => ({ a: param.h, b: param.g }),
  f2
)({ a: "", g: 0 });

I'm unable to reproduce any of your examples. Could you please share them in codesanbox? just like you shared your first example.

P.S. It seems that you want to use function composition. If yes, please take a look on pipe and compose examples.