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

Typescript error when typing submit handler function #3521

Closed
5 tasks done
slauzinho opened this issue Oct 11, 2021 · 9 comments
Closed
5 tasks done

Typescript error when typing submit handler function #3521

slauzinho opened this issue Oct 11, 2021 · 9 comments
Labels
duplicate This issue or pull request already exists

Comments

@slauzinho
Copy link

What happened?

On the latest version 4.5.x we can no longer do this:

<Form @submit="handleSubmit">
    <Field as="input" label="Name" name="firstname" />
    <button type="submit">Submit</button>
  </Form>

const handleSubmit = (data: { name: string }) => {
  console.log(data);
};

Typescript errors out with:

Type '(data: {    name: string;}) => void' is not assignable to type 'SubmissionHandler<GenericFormValues, unknown>'.
  Types of parameters 'data' and 'values' are incompatible

This seems to be because before the @submit had the type

submit: (...payload: any[]) => void

and now it has the type:

SubmissionHandler<GenericFormValues, unknown>

Reproduction steps

1- Provide a typed function to @submit

Version

Vue.js 3.x and vee-validate 4.x

What browsers are you seeing the problem on?

  • Firefox
  • Chrome
  • Safari
  • Microsoft Edge

Relevant log output

Type '(data: {    name: string;}) => void' is not assignable to type 'SubmissionHandler<GenericFormValues, unknown>'.
  Types of parameters 'data' and 'values' are incompatible.

Demo link

https://github.com/slauzinho/vee-validate-typescript-error/blob/master/src/App.vue

Code of Conduct

@logaretm logaretm added the duplicate This issue or pull request already exists label Oct 11, 2021
@logaretm
Copy link
Owner

logaretm commented Oct 11, 2021

This came up before in #3510

Since there is no concept of generic components in TypeScript for Vue.js (not directly), the accurate type for the submitted data would be Record<string, unknown> or Record<string, any> because you can just add a field in the template and typescript won't pick that up nor it won't alert you that the field you expected was removed.

For that case, useField is better because you can specify the form values that you expect.

const { handleSubmit } = useForm<{ name: string; }>();

const onSubmit = handleSubmit(values => {
  values.name; // string
});

I could expose a hack that would allow you to create a typed version of the Form component but I'm not sure if this is a pattern I want to encourage.

@Psycarlo
Copy link

Psycarlo commented Nov 6, 2021

Is there a full example anywhere?

Thanks

@nj-sketch
Copy link

For anyone looking for example, I solved it using useForm

<form @submit="onSubmit">
    // rest of the code
</form>

<script lang="ts">
import { defineComponent } from "vue";
import { useForm } from "vee-validate";

export default defineComponent({
  setup() {
    const { handleSubmit} = useForm();

    const onSubmit = handleSubmit((values) => {
      console.log(values);
    });

    return {
      onSubmit,
    };
  },
});
</script>

@SamroodAli
Copy link

SamroodAli commented Oct 26, 2022

if you are using yup, I made a helper function to get the submit function with types from a yup schema

function getSubmitFn<Schema extends Yup.ObjectSchema<Record<string, any>>>(
  _: Schema,
  callback: (values: Yup.InferType<Schema>) => void
) {
  return (values: Record<string, any>) => {
    return callback(values);
  };
}
const schema = Yup.object().shape({
  emailId: Yup.string().email().required().label("Email Address"),
  password: Yup.string().required().label("Password").min(8),
});

const submit = getSubmitFn(schema, (values) => {
  console.log(values.emailId);
  console.log(values.password) // you get types from the schema here
}); // submit callback only gets called after validation
<Form @submit="submit" :validation-schema="schema">
  ///    
</Form>

@qimolin
Copy link

qimolin commented Apr 11, 2023

Using the code from @SamroodAli I came up with this for zod schemas also adding the ctx so we can use the actions inside the function:

function getSubmitFn<Schema extends z.ZodObject<Record<string, any>>>(
  _: Schema,
  callback: (values: z.infer<Schema>, ctx: SubmissionContext) => void
) {
  return (values: Record<string, any>, ctx: SubmissionContext): void => {
    callback(values, ctx);
  };
}
export const schema = z.object({
  email: z
    .string({
      required_error: "Email is required",
      invalid_type_error: "Email must be a string",
    })
    .email(),
  password: z.string({
    required_error: "Password is required",
    invalid_type_error: "Password must be a string",
  }),
});

const validationSchema = toFormValidator(schema);

const submit = getSubmitFn(schema, (values, ctx) => {
  console.log(values.email);
  console.log(ctx);
})
<Form @submit="submit" :validation-schema="validationSchema">
  ///    
</Form>

@abh1nash
Copy link

Since there is no concept of generic components in TypeScript for Vue.js (not directly), the accurate type for the submitted data would be Record<string, unknown> or Record<string, any> because you can just add a field in the template and typescript won't pick that up nor it won't alert you that the field you expected was removed.

Now since you can create generic components in Vue 3.3 - is this possible?

@nakorndev
Copy link

Since there is no concept of generic components in TypeScript for Vue.js (not directly), the accurate type for the submitted data would be Record<string, unknown> or Record<string, any> because you can just add a field in the template and typescript won't pick that up nor it won't alert you that the field you expected was removed.

Now since you can create generic components in Vue 3.3 - is this possible?

image

@logaretm The official document does not explain how to use the generic with @submit with the <Form> component. Could you please provide an update on the solution? I'm currently stuck.

@logaretm
Copy link
Owner

You can't. The Form component isn't generic and wouldn't work. I have not taken a look at how Generic components can be typed programmatically or even if it is possible since we use render functions and not a template syntax.

@bechtold
Copy link

bechtold commented Jul 8, 2024

In case someone stumbles across here. I'm using yup and this is what worked for me:

Placing the schema into a variable, note that this is not yet toTypedSchema:
Screenshot 2024-07-08 at 13 38 59

For regular typing use toTypedSchema():
Screenshot 2024-07-08 at 13 39 51

Then I pass the values in the to the callback of onSubmit:
Screenshot 2024-07-08 at 13 42 10

In my submit function, I can then infer the type:
Screenshot 2024-07-08 at 13 42 30

This is described in the docs of yup, but it took me a while to understand it:
https://github.com/jquense/yup?tab=readme-ov-file#getting-started

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
duplicate This issue or pull request already exists
Projects
None yet
Development

No branches or pull requests

9 participants