@volverjs/form-vue
Version:
Vue 3 Forms with @volverjs/ui-vue
126 lines (123 loc) • 5.26 kB
text/typescript
import {
ZodDefault,
ZodObject,
ZodEffects,
ZodSchema,
ZodNullable,
ZodOptional,
ZodRecord,
ZodArray,
} from 'zod'
import type { z, AnyZodObject, ZodTypeAny } from 'zod'
import type { EffectType, FormSchema } from './types'
export function defaultObjectBySchema<Schema extends FormSchema>(schema: Schema, original: Partial<z.infer<Schema>> & Record<string, unknown> = {}): Partial<z.infer<Schema>> {
const getSchemaInnerType = <Type extends ZodTypeAny>(
schema: EffectType<Type>,
) => {
let toReturn = schema
while (toReturn instanceof ZodEffects) {
toReturn = toReturn.innerType()
}
if (toReturn instanceof ZodOptional) {
toReturn = toReturn._def.innerType
}
return toReturn
}
const isSchemaOptional = <Type extends ZodTypeAny>(
schema:
| Type
| ZodEffects<Type>
| ZodEffects<ZodEffects<Type>>
| ZodOptional<Type>,
) => {
let toReturn = schema
while (toReturn instanceof ZodEffects) {
toReturn = toReturn.innerType()
}
if (toReturn instanceof ZodOptional) {
return true
}
return false
}
const innerType = getSchemaInnerType<AnyZodObject>(schema)
const unknownKeys
= innerType instanceof ZodObject
? innerType._def.unknownKeys === 'passthrough'
: false
return {
...(unknownKeys ? original : {}),
...Object.fromEntries(
(Object.entries(innerType.shape) as [string, ZodTypeAny][]).map(
([key, subSchema]) => {
const originalValue = original[key]
const isOptional = isSchemaOptional(subSchema)
let innerType = getSchemaInnerType(subSchema)
let defaultValue: Partial<z.infer<Schema>> | undefined
if (innerType instanceof ZodDefault) {
defaultValue = innerType._def.defaultValue()
innerType = innerType._def.innerType
}
if (
originalValue === null
&& innerType instanceof ZodNullable
) {
return [key, originalValue]
}
if ((originalValue === undefined || originalValue === null) && isOptional) {
return [key, defaultValue]
}
if (innerType instanceof ZodSchema) {
const parse = subSchema.safeParse(originalValue)
if (parse.success) {
return [key, parse.data ?? defaultValue]
}
}
if (
innerType instanceof ZodArray
&& Array.isArray(originalValue)
&& originalValue.length
) {
const arrayType = getSchemaInnerType(innerType._def.type)
if (arrayType instanceof ZodObject) {
return [
key,
originalValue.map((element: unknown) =>
defaultObjectBySchema(
arrayType,
(element && typeof element === 'object'
? element
: undefined) as Partial<
typeof arrayType
>,
),
),
]
}
}
if (innerType instanceof ZodRecord && originalValue) {
const valueType = getSchemaInnerType(innerType._def.valueType)
if (valueType instanceof ZodObject) {
return [key, Object.keys(originalValue).reduce((acc: Record<string, unknown>, recordKey: string) => {
acc[recordKey] = defaultObjectBySchema(valueType, (originalValue as Record<string, unknown>)[recordKey] as Partial<any> & Record<string, unknown>)
return acc
}, {})]
}
}
if (innerType instanceof ZodObject) {
return [
key,
defaultObjectBySchema(
innerType,
originalValue
&& typeof originalValue === 'object'
? (originalValue as Partial<z.infer<Schema>> & Record<string, unknown>)
: defaultValue,
),
]
}
return [key, defaultValue]
},
),
),
} as Partial<z.infer<Schema>>
}