modular-forms
modular-forms copied to clipboard
[Qwik] Type checking for forms with variants
Let's say I have the following schema with multiple variants requiring different fields based on the placeType
field.
Here's the simplified version so I can better refer to it below:
const BaseSchema = v.object({
name: v.pipe(v.string(), v.nonEmpty()),
coordinates: v.object({
latitude: v.number(),
longitude: v.number(),
}),
thumbnailUrl: v.optional(v.pipe(v.string(), v.url())),
websiteUrl: v.optional(v.pipe(v.string(), v.url())),
});
const FeaturesSchema = v.object({
features: v.pipe(v.array(v.picklist(PlaceFeatures)), v.minLength(1)),
});
const DescriptionSchema = v.object({
description: v.optional(v.string()),
});
const AddActivitySchema = v.object({
...BaseSchema.entries,
...FeaturesSchema.entries,
});
const AddDestinationSchema = v.object({
...BaseSchema.entries,
...FeaturesSchema.entries,
...DescriptionSchema.entries,
});
export const AddPlaceSchema = v.variant("placeType", [
v.object({
placeType: v.literal("activity"),
...AddActivitySchema.entries,
}),
v.object({
placeType: v.literal("destination"),
...AddDestinationSchema.entries,
}),
v.object({
placeType: v.picklist(PlaceTypes),
...BaseSchema.entries,
}),
]);
export type AddPlaceForm = v.InferInput<typeof AddPlaceSchema>;
I want to use this schema to build a form in Qwik, but I'm having difficulties taming the Typescript compiler because it's always inferring that features
is an undefined
field or never
.
Here's a minimal example to illustrate this issue:
const AddPlaceForm = component$(
() => {
const [addPlaceForm, { Form, Field }] = useForm<AddPlaceForm>({
loader: {
value: {
name: "",
thumbnailUrl: undefined,
websiteUrl: undefined,
coordinates: placeCoordinates,
placeType: undefined,
features: [],
description: undefined,
},
},
action: useAddPlaceAction(),
validate: valiForm$(AddPlaceSchema),
});
const placeType = useComputed$(() => getValue(addPlaceForm, "placeType"));
return (
<Form>
{placeType.value === "activity" && (
<>
{placeFeatures.value.map((feature) => (
<Field
name="features"
type="string[]"
key={`feature-${feature}`}
>
{(field, props) => (
<Checkbox
{...props}
value={feature}
checked={field.value?.includes(feature)}
>
{feature}
</Checkbox>
)}
</Field>
))}
</>
)}
</Form>
);
});
Which will yield the following errors:
src/components/map/places/add-place-modal.tsx:218:23 - error TS2322: Type 'string' is not assignable to type 'undefined'.
218 type="string[]"
~~~~
../../node_modules/@modular-forms/qwik/dist/types/components/Field.d.ts:20:5
20 type: FieldType<FieldPathValue<TFieldValues, TFieldName>>;
~~~~
The expected type comes from property 'type' which is declared here on type 'IntrinsicAttributes & Pick<Partial<Omit<FieldProps<{ name: string; coordinates: { latitude: number; longitude: number; }; features: ("workspace" | "skiing" | "snowshoeing" | "nordic-skiing" | ... 8 more ... | "bathroom")[]; placeType: "activity"; link?: string | undefined; th...'
src/components/map/places/add-place-modal.tsx:225:49 - error TS2339: Property 'includes' does not exist on type 'never'.
225 checked={field.value?.includes(feature)}
Do you have an (elegant) recommendation on how to satisfy the compiler and let it know that we want a specific variant of the form in that case?