Migrating forms to TanStack Form. Common scenario: fields that are required on submit but have no valid initial value (e.g. a Date picker the user must choose, or a select that maps to a Zod enum).
Since TanStack Form infers field types from defaultValues, setting endDate: undefined or product: undefined makes the inferred type undefined — not Date or string. This creates a mismatch with the validation schema where these fields are required.
I can see two workarounds, both with significant drawbacks:
Option 1: Type-cast defaultValues
const defaultValues = {
product: undefined,
endDate: undefined,
startDate: new Date(),
} satisfies Partial<FormValues> as unknown as FormValues
Gets the types to line up, but the double-cast (as unknown as) is messy and still isn't perfect — you're lying to the type system and have to remember the field is actually undefined at runtime despite what the types say.
Option 2: Weaken the schema to match the initial state
Instead of z.date(), use z.string() with z.iso.date() and parse it. Instead of z.enum([...]), use z.string().refine(x => MyEnum.parse(x)) or similar. This avoids the type mismatch by making the schema's input type string (which can start as ""), but:
- You lose the strong enum/Date typing at the form field level
- It pushes validation complexity into the schema just to work around a defaultValues limitation
- For dates specifically, locking into ISO string representation feels limiting compared to working with Date objects directly
The core question: Is there a recommended first-class pattern for "this field starts empty but must be present and valid on submit"? Something like a type-level split between defaultValues type and onSubmit type, or a built-in initiallyOptional concept? Or is one of the above workarounds the intended approach?