Created
January 6, 2023 21:09
-
-
Save kentcdodds/f9fe6995e2c8dea9bbd51da21cfb0a0c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { z } from 'zod' | |
export function preprocessFormData<Schema extends z.ZodTypeAny>( | |
formData: FormData, | |
schema: Schema, | |
) { | |
const shape = getShape(schema) | |
return mapObj(shape, ([name, propertySchema]) => | |
transformFormDataValue( | |
getFormValue(formData, String(name), propertySchema), | |
propertySchema, | |
), | |
) | |
} | |
type FormDataValue = FormDataEntryValue | FormDataEntryValue[] | null | |
function getFormValue( | |
formData: FormData, | |
name: string, | |
schema: z.ZodTypeAny, | |
): FormDataValue { | |
if (schema instanceof z.ZodEffects) { | |
return getFormValue(formData, name, schema.innerType()) | |
} else if (schema instanceof z.ZodOptional) { | |
return getFormValue(formData, name, schema.unwrap()) | |
} else if (schema instanceof z.ZodDefault) { | |
return getFormValue(formData, name, schema.removeDefault()) | |
} else if (schema instanceof z.ZodArray) { | |
return formData.getAll(name) | |
} else { | |
return formData.get(name) | |
} | |
} | |
function transformFormDataValue( | |
value: FormDataValue, | |
propertySchema: z.ZodTypeAny, | |
): unknown { | |
if (propertySchema instanceof z.ZodEffects) { | |
return transformFormDataValue(value, propertySchema.innerType()) | |
} else if (propertySchema instanceof z.ZodOptional) { | |
return transformFormDataValue(value, propertySchema.unwrap()) | |
} else if (propertySchema instanceof z.ZodDefault) { | |
return transformFormDataValue(value, propertySchema.removeDefault()) | |
} else if (propertySchema instanceof z.ZodArray) { | |
if (!value || !Array.isArray(value)) { | |
throw new Error('Expected array') | |
} | |
return value.map(v => transformFormDataValue(v, propertySchema.element)) | |
} else if (propertySchema instanceof z.ZodObject) { | |
throw new Error('Support object types') | |
} else if (propertySchema instanceof z.ZodBoolean) { | |
return Boolean(value) | |
} else if (propertySchema instanceof z.ZodNumber) { | |
return Number(value) | |
} else { | |
return value | |
} | |
} | |
function getShape<Schema extends z.ZodTypeAny>(schema: Schema) { | |
let shape = schema | |
while (shape instanceof z.ZodObject || shape instanceof z.ZodEffects) { | |
shape = | |
shape instanceof z.ZodObject | |
? shape.shape | |
: shape instanceof z.ZodEffects | |
? shape._def.schema | |
: null | |
if (shape === null) { | |
throw new Error(`Could not find shape`) | |
} | |
} | |
return shape | |
} | |
function mapObj<Key extends string, Value, MappedValue>( | |
obj: Record<Key, Value>, | |
cb: (entry: [Key, Value]) => MappedValue, | |
): Record<Key, MappedValue> { | |
return Object.entries(obj).reduce((acc, entry) => { | |
acc[entry[0] as Key] = cb(entry as [Key, Value]) | |
return acc | |
}, {} as Record<Key, MappedValue>) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment