My form structure involves a base interface which is extended by each page to create a more detailed form. Now, I am looking to develop a child component that can generate a form using the extended template.
view minimal reproducible example
// childComponent.tsx
export default function ProductSourceFormTemplate<T extends IProductSourceBase>({ formSchema }: {
formSchema: z.ZodType<T>,
}) {
type FormValues = z.infer<typeof formSchema>
const defaultValues: T = useMemo<T>(() => ({
sourceId: ""
}) as T, [searchParams])
const form = useForm<FormValues>({
resolver: zodResolver(formSchema),
defaultValues: defaultValues,
});
// display form
}
However, an error occurs with defaultValues:
Type 'T' is not assignable to type 'AsyncDefaultValues<T> | DefaultValues<T> | undefined'.
Type 'IProductSourceBase' is not assignable to type 'AsyncDefaultValues<T> | DefaultValues<T> | undefined'.
Type 'IProductSourceBase' is not assignable to type 'DefaultValues<T>'.ts(2322)
In addition, none of the names are recognized when attempting to create form elements.
What I aim for is the parent component to create productBaseSchema.extend({})
, which is a z.ZodObject<{}>
, and then pass this extended schema to the child as
const extendedSchema: z.ZodObject<z.objectUtil.extendShape<{}>
. The child should also provide onSubmit and render functions in its props so the parent only deals with additional fields.
For example:
export interface IProductSourceBase {
productId: string;
sourceId: number;
sourceMeta?: any;
}
const productSourceBaseSchema = z.object({
productId: z.string().nonempty(translate("Product ID is required")),
sourceId: z.coerce.number().int(translate("Source ID must be an integer")).gt(0, translate("A source must be selected")),
});
const extendedSchema = productSourceBaseSchema.extend({
sourceMeta: z.object({
sourceUrl: z.string().url(translate("This is not a valid URL")).nonempty(translate("Url is required")).refine(
(url) => /\/spreadsheets\/d\/([a-zA-Z0-9-_]{16,44})/.test(url), // Test for valid Spreadsheet ID
{
message: translate("This is not a valid Google Spreadsheet ID"), // Custom error message
}
),
cell: z.string().nonempty(translate("Cell number is required")).refine(
(cell) => /^[A-Za-z]+[1-9]\d*$/.test(cell), // Check if the cell matches the pattern
{
message: translate("Invalid cell reference format"), // Custom error message for invalid format
}
),
}),
});