Environment
- Operating System: Darwin
- Node Version: v24.5.0
- Nuxt Version: 4.1.2
- CLI Version: 3.28.0
- Nitro Version: 2.12.5
- Package Manager: pnpm@10.15.0
- Builder: -
- User Config: compatibilityDate, ssr, alias, typescript, nitro, vite, modules, i18n, css, hooks, icon, runtimeConfig, $development, $test
- Runtime Modules: @nuxt/icon@2.0.0, @nuxt/ui@4.0.0, @nuxtjs/i18n@10.1.0, nuxt-auth-utils@0.5.24
- Build Modules: -
Is this bug related to Nuxt or Vue?
Nuxt
Package
v4.x
Version
4.0.0
Reproduction
https://codesandbox.io/p/devbox/epic-shamir-l5ccfm
Description
The code as simple as this:
<script setup lang="ts">
import * as z from "zod"
import { reactive } from "vue"
const fields = reactive({
// In UI, the file may be selected or not.
file: null as File | null,
})
const schema = z.object({
// But the form is only considered valid when the file is selected.
file: z.file(),
})
</script>
<template>
<u-form :state="fields" :schema="schema">
<u-file-upload v-model="fields.file" />
</u-form>
</template>
Triggers type error:
Type '{ file: { readonly lastModified: number; readonly name: string; readonly webkitRelativePath: string; readonly size: number; readonly type: string; arrayBuffer: () => Promise<ArrayBuffer>; slice: (start?: number | undefined, end?: number | undefined, contentType?: string | undefined) => Blob; stream: () => ReadableSt...' is not assignable to type 'Partial<{ file: File; }>'
Expected: it's allowed to have a form with a schema that requires a file to be selected.
Additional context
In fact, I believe this is a much broader problem. Why is the state typed as Partial<InferInput<S>> in the first place? The type of the fields does not always match the schema. Basically, that's the point of schema in the first place, to perform validation — and not only value, but also the type. Consider number input value:
<script setup lang="ts">
import * as z from "zod"
const fields = reactive({
// Type used by <input v-model="..." type="number">
// It saves empty string for non-numbers.
foo: "" as number | "",
})
const schema = z.object({
// But we need the actual number for the input data.
foo: z.number(),
})
</script>
<template>
<u-form :state="fields" :schema="schema">
<input v-model="fields.foo" type="number" />
</u-form>
</template>
This leads to similar type error:
Type '{ foo: number | ""; }' is not assignable to type 'Partial<{ foo: number; }>'.
Types of property 'foo' are incompatible.
Type 'number | ""' is not assignable to type 'number | undefined'.
Type 'string' is not assignable to type 'number'.
As I see it, state should be untyped/unknown.
Logs
Environment
Is this bug related to Nuxt or Vue?
Nuxt
Package
v4.x
Version
4.0.0
Reproduction
https://codesandbox.io/p/devbox/epic-shamir-l5ccfm
Description
The code as simple as this:
Triggers type error:
Expected: it's allowed to have a form with a schema that requires a file to be selected.
Additional context
In fact, I believe this is a much broader problem. Why is the
statetyped asPartial<InferInput<S>>in the first place? The type of the fields does not always match the schema. Basically, that's the point of schema in the first place, to perform validation — and not only value, but also the type. Consider number input value:This leads to similar type error:
As I see it,
stateshould be untyped/unknown.Logs