schema/src/items/object.ts

66 lines
1.7 KiB
TypeScript

import { isObject, objectClone, objectLoop } from '@dzeio/object-util'
import SchemaItem from '../SchemaItem'
import { SchemaInfer, ValidationResult } from '../types'
type ModelInfer<M extends Record<string, SchemaItem>> = {
[key in keyof M]: SchemaInfer<M[key]>
}
export default class SchemaObject<T extends Record<string, SchemaItem> = any> extends SchemaItem<ModelInfer<T>> {
public id = 'object'
public constructor(public readonly model: T) {
super([model])
}
public override parse(input: unknown, options?: { fast?: boolean }): ValidationResult<ModelInfer<T>> {
// check errors from itself
const { valid, object, errors = [] } = super.parse(input, options)
// skip checking childs if self is not valid (maybe still try to check childs whan fast is false ?)
if (!valid) {
return {
valid,
object,
errors
} as ValidationResult<ModelInfer<T>>
}
const clone = objectClone(object)
// loop through the childs
objectLoop(this.model, (childSchema, key) => {
const childValue = clone[key]
// parse the child
const child = childSchema.parse(childValue)
// add errors
if (!child.valid) {
errors.push(...child.errors.map((it) => ({
...it,
field: it.field ? `${key}.${it.field}` : key
})))
}
// @ts-expect-error while it's a generic we know by proof above that it's valid !
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
clone[key] = child.object
// skip rest of items if current one is invalid
return child.valid || !options?.fast
})
// answer !
return {
valid: errors.length === 0,
errors: errors,
object: clone
} as ValidationResult<ModelInfer<T>>
}
public override isOfType(input: unknown): input is ModelInfer<T> {
return isObject(input)
}
}