Skip to content

Instantly share code, notes, and snippets.

@OrderAndCh4oS
Created September 26, 2024 00:21
Show Gist options
  • Select an option

  • Save OrderAndCh4oS/b87785652e2bfaec74be022831079f3f to your computer and use it in GitHub Desktop.

Select an option

Save OrderAndCh4oS/b87785652e2bfaec74be022831079f3f to your computer and use it in GitHub Desktop.
Validation
interface IValidationSchema {
[property: string]: (IValidateProperty | IValidateArray | IValidateObject)
}
interface IValidateArray {
required: boolean
type: 'array',
schema: IValidationSchema
}
interface IValidateObject {
required: boolean
type: 'object',
schema: IValidationSchema
}
interface IValidateProperty {
required: boolean
validators?: IValidator[]
}
interface IValidator {
(property: string, data: any, pushError: (error: IValidationErrorMessage) => void): void
}
interface IValidationErrorMessage {
property: string;
error: string | IValidationErrorMessage[];
}
function validator(
data: any,
validationSchema: IValidationSchema,
errors: IValidationErrorMessage[] = [],
index?: number
) {
const pushError = (error: IValidationErrorMessage) => {
const property: string = typeof index === 'number'
? `${error.property}.${index}`
: error.property;
errors.push({ ...error, property })
}
const isObject = (obj: any) => Object(obj) === obj && !Array.isArray(obj);
for (const [property, validate] of Object.entries(validationSchema)) {
if (validate.required && !(property in data)) {
pushError({property, error: 'Is required'});
continue;
}
if (!(property in data)) continue;
if ('validators' in validate && validate.validators) {
for (const validation of validate.validators) {
validation(property, data, pushError);
}
continue
}
if ('schema' in validate && validate.type === 'object') {
if (!isObject(data[property])) {
pushError({ property, error: 'Is not an object' });
continue;
}
const nextErrors: IValidationErrorMessage[] = []
validator(data[property], validate.schema, nextErrors)
if(nextErrors.length) pushError({property, error: nextErrors});
continue;
}
if ('schema' in validate && validate.type === 'array') {
if (!Array.isArray(data[property])) {
pushError({ property, error: 'Is not an array' });
continue;
}
let i = 0;
for (const obj of data[property]) {
const nextErrors: IValidationErrorMessage[] = []
validator(obj, validate.schema, nextErrors, i);
if(nextErrors.length) pushError({property, error: nextErrors});
i++;
}
}
}
const allowedKeys = Object.keys(validationSchema);
for (const property of Object.keys(data)) {
if (!allowedKeys.find((ak) => ak === property)) {
pushError({ property, error: 'Is not allowed' });
}
}
return errors;
}
const isType = (_type: string): IValidator => (property, data, pushError) => {
if (typeof data[property] !== _type) pushError({ property, error: `Is not ${_type}` });
}
const isString: IValidator = isType('string');
const isNumber: IValidator = isType('number');
const isTypeOrNull = (_type: string): IValidator => (property, data, pushError) => {
if (data[property] === null || typeof data[property] !== _type) pushError({ property, error: `Is not ${_type} or null` });
}
const isOneOfType = (types: string[]): IValidator => (property, data, pushError) => {
if (typeof data[property] in types) pushError({ property, error: `Is not one of ${types.join(', ')}` });
}
const isArray: IValidator = (property, data, pushError) => {
if (Array.isArray(data[property])) pushError({ property, error: `Is not an array` });
}
const isObject: IValidator = (property, data, pushError) => {
if (!(Object(data[property]) === data[property] && !Array.isArray(data[property]))) pushError({ property, error: `Is not an object` });
}
const minValue = (value: number): IValidator => (property, data, pushError) => {
if (data[property] < value) pushError({ property, error: 'Is less than min value' });
}
const maxValue = (value: number): IValidator => (property, data, pushError) => {
if (data[property] > value) pushError({ property, error: 'Is greater than max value' });
}
const minLength = (value: number): IValidator => (property, data, pushError) => {
if (data[property].length < value) pushError({ property, error: 'Is too short' });
}
const maxLength = (value: number): IValidator => (property, data, pushError) => {
if (data[property].length > value) pushError({ property, error: 'Is too long' });
}
/**
* Test one validators
*/
const demoSchemaOne: IValidationSchema = {
required: {
required: true
},
lowNum: {
required: true,
validators: [isNumber, minValue(3), maxValue(10)]
},
highNum: {
required: true,
validators: [isNumber, minValue(3), maxValue(10)]
},
notNum: {
required: true,
validators: [isNumber, minValue(3), maxValue(10)]
},
shortStr: {
required: true,
validators: [isString, minLength(3), maxLength(10)]
},
longStr: {
required: true,
validators: [isString, minLength(3), maxLength(10)]
},
notStr: {
required: true,
validators: [isString, minLength(3), maxLength(10)]
},
shortArr: {
required: true,
validators: [isArray, minLength(3), maxLength(10)]
},
longArr: {
required: true,
validators: [isArray, minLength(3), maxLength(5)]
},
notObj: {
required: true,
validators: [isObject]
}
}
const demoDataOne = {
lowNum: 1,
highNum: 11,
notNum: 'a',
shortStr: 'aa',
longStr: 'aaaaaaaaaaaaa',
notStr: 1,
shortArr: [1, 2],
longArr: [1, 2, 3, 4, 5, 6],
notObj: 10,
notAllowed: 'oh no'
}
const testOne = validator(demoDataOne, demoSchemaOne);
console.log(testOne);
console.log('isValid:', testOne.length === 0);
/**
* Test two prop + arr
*/
const demoSchemaTwo: IValidationSchema = {
unit: {
required: true,
validators: [minValue(3), maxValue(10)]
},
arr: {
required: true,
type: 'array',
schema: {
arrUnit: {
required: true,
validators: [minValue(3), maxValue(10)]
}
}
}
}
const demoDataTwo = {
unit: 11,
arr: [{ arrUnit: 1 }, { arrUnit: 74 }]
}
const testTwo = validator(demoDataTwo, demoSchemaTwo);
console.log(JSON.stringify(testTwo));
console.log('isValid:', testTwo.length === 0);
/**
* Test three prop + obj
*/
const demoSchemaThree: IValidationSchema = {
unit: {
required: true,
validators: [minValue(3), maxValue(10)]
},
obj: {
required: true,
type: 'object',
schema: {
objUnit: {
required: true,
validators: [minValue(3), maxValue(10)]
}
}
}
}
const demoDataThree = {
unit: 11,
obj: { objUnit: 1 },
somethingElse: 'blah'
}
const testThree = validator(demoDataThree, demoSchemaThree);
console.log(JSON.stringify(testThree));
console.log('isValid:', testThree.length === 0);
/**
* Test four prop + obj + arr VALID
*/
const demoSchemaFour: IValidationSchema = {
unit: {
required: true,
validators: [minValue(3), maxValue(10)]
},
obj: {
required: true,
type: 'object',
schema: {
objUnit: {
required: true,
validators: [minValue(3), maxValue(10)]
}
}
},
arr: {
required: true,
type: 'array',
schema: {
arrUnit: {
required: true,
validators: [minValue(3), maxValue(10)]
}
}
}
}
const demoDataFour = {
unit: 10,
obj: { objUnit: 4 },
arr: [{ arrUnit: 3 }, { arrUnit: 6 }],
}
console.log('isValid:', validator(demoDataFour, demoSchemaFour).length === 0);
/**
* Test five required props
*/
const demoSchemaFive: IValidationSchema = {
unit: {
required: true,
validators: [minValue(3), maxValue(10)]
},
unitTwo: {
required: true,
validators: [minValue(3), maxValue(10)]
},
obj: {
required: true,
type: 'object',
schema: {
objUnit: {
required: true,
validators: [minValue(3), maxValue(10)]
}
}
},
arr: {
required: true,
type: 'array',
schema: {
arrUnit: {
required: true,
validators: [minValue(3), maxValue(10)]
}
}
}
}
const demoDataFive = {
}
const testFive = validator(demoDataFive, demoSchemaFive);
console.log(JSON.stringify(testFive));
console.log('isValid:', testFive.length === 0);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment