sveltekit-superforms icon indicating copy to clipboard operation
sveltekit-superforms copied to clipboard

Enums without default values

Open Nelhoa opened this issue 1 year ago • 13 comments

Thanks for repairing #333 But would it be possible to not have default values for enums ? I would like to have my select fields empty when the page load. Instead I’m forced to set values to undefined with typescript errors in the script tags.

Severity : Minor

Nelhoa avatar Feb 14 '24 08:02 Nelhoa

It's a matter of type-safety - if the type is YourEnum (as it is with required fields) compared to YourEnum | undefined (optional fields), then it must have a value, it cannot be undefined.

I'm considering adding an enumProxy or similar to handle this.

ciscoheat avatar Feb 14 '24 10:02 ciscoheat

Thanks ! I’ll wait to see how you handle this 👍

Nelhoa avatar Feb 14 '24 14:02 Nelhoa

To make it work with all validation libraries, you have to be able to map the inferred schema type T to a type that extends certain fields in it with for example undefined or even an empty string. But I don't think that's the whole solution, as you need to specify the default (empty) values somehow too.

The best workaround I can come up with is to modify the adapter defaults.

const schema = z.object({
  dir: z.enum(['north', 'south', 'east', 'west'])
});

const adapter = zod(schema);
adapter.defaults.dir = '' as 'north';

const form = await superValidate(adapter);

ciscoheat avatar Feb 17 '24 06:02 ciscoheat

Don’t you think that it may run into the same issue ?

I’m not sure to understand the role of the adapter.

If dir = '' instead of undefined, the select fields will consider themselves as selected ?

If the user click on submit he won’t have an HTML alert with a tooltip on the select field, but he’s going to have a Zod error message telling the value doesn’t fit the enum.

Nelhoa avatar Feb 19 '24 08:02 Nelhoa

Modifying the adapter defaults makes it possible to have a required enum field, while still having a default value that isn't part of the enum.

dir cannot be undefined in the above schema, but an empty string will coerce with the strings in the enum, so you don't have to cast with as unknown. In either case, no option will be initially selected.

ciscoheat avatar Feb 19 '24 09:02 ciscoheat

Looks like this (at least the specific issue I was encountering) may have been fixed in 2.6.1

shellicar avatar Feb 25 '24 08:02 shellicar

This also works in the server, but it would be very nice if it was handled natively by superForms:

// +page.server.ts
const form = await superValidate(zod(mySchema));
form.data.dir = undefined as unknown as Direction;

return { form }

wesharper avatar Feb 29 '24 06:02 wesharper

Our app has giant forms with dozens of required select inputs, so having to do this manually for each form is a pain.

Also, we have a handful of large forms that will autosave a draft using a partial schema and validate a full schema and publish a record on submit. I will let you know what that mess looks like once I start migrating those larger forms over.

wesharper avatar Feb 29 '24 06:02 wesharper

I suggest making a helper function/wrapper for superValidate, as this problem won't easily go away, due to how the JSON schema generators are detecting required fields.

ciscoheat avatar Mar 01 '24 14:03 ciscoheat

Best workaround currently, which keeps things in the schema and with the same type, so it won't cause a native type error:

const schema = z.object({
  dir: z.enum(['north', 'south', 'east', 'west']).default('' as 'north')
});

ciscoheat avatar Mar 20 '24 20:03 ciscoheat

Thanks but this is dirty x)

Nelhoa avatar Mar 21 '24 08:03 Nelhoa

You can't have it both ways, if you want type-safety you must add '' as a value to the enum, and invalidate it in a refine function.

ciscoheat avatar Mar 21 '24 09:03 ciscoheat

This is type-safe:

const schema = z.object({
  dir: z
    .enum(['', 'north', 'south', 'east', 'west'])
    .refine((dir) => dir != '', 'No direction specified')
});

ciscoheat avatar Mar 21 '24 20:03 ciscoheat