vee-validate
vee-validate copied to clipboard
Type Error `errorBag` using array in form
What happened?
When I use errorBag in useForm with array format, type-check (vue-tsc --noEmit) is failed. So when the following code is executed, it points out that the type definition at point errorBag[`timeCandidates[${index}].time`] and errorBag[`timeCandidates[${index}].num`] are incorrect.
Sample Code
<template>
<div>
<h1>This is an form page</h1>
<form @submit="onSubmit">
<div>
<label>Candidates</label>
<ul v-for="(_, index) in fields" :key="`timeCandidates_${index}`">
<li>
<div class="candidate">
<Field
:id="`timeCandidates_${index}_time`"
:name="`timeCandidates[${index}].time`"
v-slot="{ field, handleBlur }"
>
<input v-bind="field" @blur="handleBlur" class="input" />
</Field>
<Field
:id="`timeCandidates_${index}_num`"
:name="`timeCandidates[${index}].num`"
v-slot="{ field, handleBlur }"
>
<input v-bind="field" @blur="handleBlur" class="input" type="number" />
</Field>
<div @click="remove(index)">delete</div>
</div>
<div
class="errors"
v-for="error in errorBag[`timeCandidates[${index}].time`]"
:key="`timeCandidates_${index}_time_${error}`"
>
{{ error }}
</div>
<div
class="errors"
v-for="error in errorBag[`timeCandidates[${index}].num`]"
:key="`timeCandidates_${index}_num_${error}`"
>
{{ error }}
</div>
</li>
</ul>
<div @click="push(initialTimeCandidate)">add</div>
</div>
<button :disabled="!meta.valid">Submit</button>
</form>
</div>
</template>
<script setup lang="ts">
import { toTypedSchema } from '@vee-validate/zod'
import { Field, useFieldArray, useForm } from 'vee-validate'
import { watch } from 'vue'
import { z } from 'zod'
const FormSchema = z.object({
timeCandidates: z.array(
z.object({
time: z.string().regex(/^[0-9]{2}:[0-9]{2}$/, {
message: 'input hh:mm'
}),
num: z.coerce.number().min(3)
})
)
})
const initialTimeCandidate = { time: '', num: 0 } as const satisfies z.infer<
typeof FormSchema
>['timeCandidates'][number]
const { meta, errorBag, handleSubmit } = useForm<z.infer<typeof FormSchema>>({
validationSchema: toTypedSchema(FormSchema),
initialValues: {
timeCandidates: [initialTimeCandidate]
}
})
const { push, remove, fields } =
useFieldArray<z.infer<typeof FormSchema>['timeCandidates'][number]>('timeCandidates')
const onSubmit = handleSubmit((values) => {
console.log(values)
alert(JSON.stringify(values, null, 2))
})
watch(errorBag, (value) => {
console.log('[debug] errorBag: ', value)
})
</script>
<style>
.input {
border: 1px solid #ccc;
border-radius: 4px;
padding: 8px;
margin: 8px;
}
.candidate {
display: flex;
gap: 8px;
align-items: center;
}
.errors {
color: red;
}
</style>
Type-check output vue-tsc --build --force
src/views/FormView.vue:28:31 - error TS7053: Element implicitly has an 'any' type because expression of type '`timeCandidates[${number}].time`' can't be used to index type 'Partial<Record<"timeCandidates" | `timeCandidates.${number}` | `timeCandidates.${number}.time` | `timeCandidates.${number}.num`, string[]>>'.
28 v-for="error in errorBag[`timeCandidates[${index}].time`]"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/views/FormView.vue:35:31 - error TS7053: Element implicitly has an 'any' type because expression of type '`timeCandidates[${number}].num`' can't be used to index type 'Partial<Record<"timeCandidates" | `timeCandidates.${number}` | `timeCandidates.${number}.time` | `timeCandidates.${number}.num`, string[]>>'.
35 v-for="error in errorBag[`timeCandidates[${index}].num`]"
However, on runtime, there is no problem.
When the object is checked, it stores a value different from the type definition.
So, even if you modify it according to type-check, the validation error messages will not be displayed correctly on the runtime.
- v-for="error in errorBag[`timeCandidates[${index}].time`]"
+ v-for="error in errorBag[`timeCandidates.${index}.time`]"
- v-for="error in errorBag[`timeCandidates[${index}].num`]"
+ v-for="error in errorBag[`timeCandidates.${index}.num`]"
I think the problem is in the PathInternal type definition.
Reproduction steps
...
Version
Vue.js 3.x and vee-validate 4.x
What browsers are you seeing the problem on?
- [ ] Firefox
- [X] Chrome
- [ ] Safari
- [ ] Microsoft Edge
Relevant log output
No response
Demo link
https://stackblitz.com/edit/vee-validate-issue-template-nsbdfw?file=src%2FApp.vue
When you try type-check in terminal, you can see error.
vue-tsc --noEmit
N/A
Code of Conduct
- [X] I agree to follow this project's Code of Conduct
Any update on this?
I added StackBlitz to the issue summary. So, you can check this.
This is a tough issue to fix, because we have 2 path syntaxes that get confused.
Closing this in favor of tracking it in #4295, this is probably a v5 change that will be breaking since we are more likely to drop using [] rather than support it everywhere.