logaretm / vee-validate

✅ Painless Vue forms
https://vee-validate.logaretm.com/v4
MIT License
10.74k stars 1.26k forks source link

Infer model type from schema when using defineField() #4808

Open bechtold opened 2 months ago

bechtold commented 2 months ago

Is your feature request related to a problem? Please describe.

I'm using typescript, yup, toTypedSchema(), and it works like a charm after getting used to values from useForm() still having | undefined types, but values from onSubmit() having the required type.

The problem: the model that is returned from defineField() has type Ref<unknown>.

Screenshot 2024-07-08 at 13 02 57

This is annoying when assigning it to components typed model:

Screenshot 2024-07-08 at 13 03 12

Casting the type when passing it to the component works, but is not nice I guess. Especially if you use the model multiple times, you don't want to cast it every time. Also, my understanding is, that casting would not bring the benefits that I'm looking for from typescript.

Screenshot 2024-07-08 at 13 13 25

The same goes for using the ref in code:

Screenshot 2024-07-08 at 13 18 44

Similar when defining, for example, a boolean and using it in a v-if. It would be possible to use values.variableName, but why not use the model?

Especially because the type is defined in the schema:

Screenshot 2024-07-08 at 13 05 10

After fiddling around a bit, I found out that adding the field's path to the generic type of defineField() does help to infer the proper type.

Screenshot 2024-07-08 at 13 03 39

Also in code the ref's type is inferred correctly:

Screenshot 2024-07-08 at 13 23 28

Describe the solution you'd like

It would be nice if the model's type is inferred automatically instead of manually having to do it. Maybe I'm missing something, so I'm grateful for any hints.

Describe alternatives you've considered

Passing the field's path to infer the type from helps, but is redundant: defineField<"pathToField">("pathToField")

logaretm commented 1 month ago

it is possible that it is getting confused by the MaybeRefOrGetter thrown in there. I didn't encounter this in the tests nor in my daily usage of this.

Can you create a minimal reproduction for this?

zumm commented 1 month ago

@logaretm Probably this is happening because of incorrect typed vuetifyConfig (we can see on the screenshot author is using it)

import * as yup from 'yup';
import { useForm, type LazyInputBindsConfig } from 'vee-validate';
import { toTypedSchema } from '@vee-validate/yup';

const FormSchema = yup.object({
  fieldOne: yup.number().required(),
  fieldTwo: yup.number().required(),
});

const { defineField } = useForm({
  validationSchema: toTypedSchema(FormSchema),
});

const vuetifyBindsConfig: LazyInputBindsConfig = (state) => ({
  props: {
    'error-messages': state.errors,
  },
});

const [fieldOne] = defineField('fieldOne', vuetifyBindsConfig); // unknow
const [fieldTwo] = defineField('fieldTwo'); // number | undefined

// or
const [fieldOne] = defineField<'fieldOne'>('fieldOne', vuetifyBindsConfig); // number | undefined

Problem is i don't know correct type for vuetifyBindsConfig. :)

zumm commented 1 month ago

And there is another issue with defineField typing. It defines type of object fields as PartialObjectDeep<T, {}> | undefined so they can't be used with components which expect T as model.

import * as yup from 'yup';
import { useForm from 'vee-validate';
import { toTypedSchema } from '@vee-validate/yup';

const FormSchema = yup.object({
  objectField: yup.object({
    id: yup.string().required()
    // ...
  })
});

const { defineField } = useForm({
  validationSchema: toTypedSchema(FormSchema),
});

const [objectField] = defineField('objectField');

// type error:
// <SomeFieldComponent v-model="objectField" />

// SomeFieldComponent.vue
defineModel<{
  id: string
  // ...
}>()