@stacksjs/ts-validation
Version:
A simple TypeScript starter kit using Bun.
105 lines (89 loc) • 3.02 kB
TypeScript
import type { ObjectValidatorType, ValidationErrorMap, ValidationNames, ValidationResult, Validator } from '../types';
export declare class ObjectValidator<T extends Record<string, any>> extends BaseValidator<T> implements ObjectValidatorType<T> {
public name: ValidationNames = 'object'
private schema: Record<string, Validator<any>> = {}
private strictMode = false
constructor(schema?: Record<string, Validator<any>>) {
super()
this.addRule({
name: 'object',
test: (value: unknown): value is T =>
typeof value === 'object' && value !== null && !Array.isArray(value),
message: 'Must be an object',
})
if (schema) {
this.shape(schema)
}
}
shape(schema: Record<string, Validator<any>>): this {
this.schema = Object.entries(schema).reduce((acc, [key, validator]) => {
if (validator instanceof BaseValidator) {
acc[key] = validator.setIsPartOfShape(true).setFieldName(key)
}
else {
acc[key] = validator
}
return acc
}, {} as Record<string, Validator<any>>)
return this.addRule({
name: 'shape',
test: (value: T) => {
if (value === null || value === undefined)
return true
if (this.strictMode) {
const schemaKeys = new Set(Object.keys(schema))
const valueKeys = Object.keys(value)
return valueKeys.every(key => schemaKeys.has(key))
}
return true
},
message: 'Invalid object shape',
})
}
strict(strict = true): this {
this.strictMode = strict
return this
}
custom(validationFn: (value: T) => boolean, message: string): this {
return this.addRule({
name: 'custom',
test: (value: T) => value === undefined || value === null || validationFn(value),
message,
})
}
validate(value: T): ValidationResult {
const result = super.validate(value)
if (!result.valid)
return result
if (Object.keys(this.schema).length > 0 && value !== null && value !== undefined) {
const errors: ValidationErrorMap = {}
let hasErrors = false
for (const [key, validator] of Object.entries(this.schema)) {
const fieldValue = value[key]
const fieldResult = validator.validate(fieldValue)
if (!fieldResult.valid) {
hasErrors = true
if (validator instanceof ObjectValidator) {
Object.entries(fieldResult.errors).forEach(([errorKey, errorValue]) => {
errors[`${key}.${errorKey}`] = errorValue
})
}
else {
const fieldErrors = Object.values(fieldResult.errors)[0]
if (fieldErrors) {
errors[key] = fieldErrors
}
}
}
}
if (hasErrors) {
return {
valid: false,
errors,
}
}
}
return result
}
}
export declare function object<T extends Record<string, any>>(schema?: Record<string, Validator<any>>): ObjectValidator<T>;