conform icon indicating copy to clipboard operation
conform copied to clipboard

Default value breaks types

Open jansedlon opened this issue 2 years ago β€’ 5 comments

Describe the bug and the expected behavior

When using for example zod discriminated union for form schema, providing default value breaks the types completely.

Conform version

v1.0.2

Steps to Reproduce the Bug or Issue

https://stackblitz.com/edit/github-qiy4lh-vn8142?file=app%2Froutes%2Ftype-inference.tsx

What browsers are you seeing the problem on?

No response

Screenshots or Videos

No response

Additional context

No response

jansedlon avatar Feb 22 '24 06:02 jansedlon

From what I understand, the defaultValue must exactly match the schema type which is a bit cumbersome and breaks other things.

A workaround I found is to fallback all string values to ?? "" even if they are optional, but that's cumbersome as well πŸ˜…

jansedlon avatar Feb 22 '24 07:02 jansedlon

It looks like Typescript is trying to infer the types from both defaultValue and onValidate. I think this will be best solved by the NoInfer which is still on beta πŸ˜…. Let me check if there is another approach that can work with earlier version.

A temporary solution is to set the type manually to stop typescript from inferring it.

const [form, fields] = useForm<z.input<typeof schema>>({
   // ...
});

edmundhung avatar Feb 22 '24 10:02 edmundhung

Yeah I've already tried to apply NoInfer directly in the conform source code but to no avail πŸ˜‚ I'll try your approach

jansedlon avatar Feb 22 '24 10:02 jansedlon

Typescript seems to infer the type correctly if you spread over the defaultValues instead of passing it to the useForm hook directly:

const [form, fields] = useForm({
    lastResult,
    onValidate({ formData }) {
      return parseWithZod(formData, { schema: LinkPageFormSchema });
    },
    defaultValue: {
      ...defaultValues
    },
});

edmundhung avatar Feb 25 '24 14:02 edmundhung

@edmundhung, I've done my best to create an isolated reproduction of the problems here: TypeScript Playground

As you can see, in most places schema type inference is working exactly opposite of what's expected. For example, if you hover over the last useForm call, you can see the first two type arguments are reversed; the inferred Schema type comes from defaultValue, while the inferred FormValue type comes from schema. This matches the behaviour I've been seeing locally.

Thankfully, passing explicit type arguments always works as expected, which I've also demonstrated in the reproduction. So for now, this is what we'll do in our codebase.

What's truly maddening is that on line 18, if you switch FormOptions<Schema, FormValue> to FormOptions<FormValue, Schema>, 6 of the 7 error cases are resolved! I have no idea why this works, because it definitely shouldn'tβ€”it is absolutely backwards. And yet, somehow, it makes up for almost every incorrect type inference. 🀷

aaronadamsCA avatar Mar 03 '24 18:03 aaronadamsCA