From 7f10ba75d2387df0e24430e294e8da98aada8b26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Jeancolas?= Date: Wed, 3 Aug 2022 15:31:54 +0200 Subject: [PATCH] Add error type and field name as properties of ValidationError --- index.d.ts | 11 ++++++++- js/TypeChecker.js | 47 ++++++++++++++++++++++++------------ js/TypeChecker.js.map | 2 +- package.json | 2 +- src/TypeChecker.ts | 55 +++++++++++++++++++++++++++++++++---------- tests/TypeChecker.ts | 22 +++++++++++++++++ 6 files changed, 108 insertions(+), 31 deletions(-) diff --git a/index.d.ts b/index.d.ts index e21df33..326e69f 100644 --- a/index.d.ts +++ b/index.d.ts @@ -8,7 +8,16 @@ export interface PropertyCheckParams { onFailure?: onFailure; } -export class ValidationError extends Error {} +export declare enum ValidationErrorType { + NullValue = "null", + MissingField = "missing", + InvalidType = "invalid" +} + +export class ValidationError extends Error { + public readonly field: string | undefined; + public readonly errorType: ValidationErrorType; +} export function TypesCheck(target: any, propertyKey: string, descriptor: TypedPropertyDescriptor): any; export function TypeCheck(type: any): any; diff --git a/js/TypeChecker.js b/js/TypeChecker.js index ff62000..9fe8d2d 100644 --- a/js/TypeChecker.js +++ b/js/TypeChecker.js @@ -1,16 +1,33 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.validate = exports.PropertyCheck = exports.TypeCheck = exports.TypesCheck = exports.ValidationError = void 0; +exports.validate = exports.PropertyCheck = exports.TypeCheck = exports.TypesCheck = exports.ValidationErrorType = exports.ValidationError = void 0; require("reflect-metadata"); const propertiesToCheck = Symbol('propertiesToCheck'); const paramsToCheck = Symbol('paramsToCheck'); class ValidationError extends Error { + constructor(message, errorType, fields) { + if (Array.isArray(fields) && fields.length > 0) { + super(`${fields.join('.')}: ${message}`); + this.field = fields[fields.length - 1]; + } + else { + super(message); + } + this.errorType = errorType; + } } exports.ValidationError = ValidationError; +var ValidationErrorType; +(function (ValidationErrorType) { + ValidationErrorType["NullValue"] = "null"; + ValidationErrorType["MissingField"] = "missing"; + ValidationErrorType["InvalidType"] = "invalid"; +})(ValidationErrorType = exports.ValidationErrorType || (exports.ValidationErrorType = {})); class InternalError extends Error { - constructor() { - super(...arguments); + constructor(message, errorType) { + super(message); this.fields = []; + this.errorType = errorType; } } function TypesCheck(target, propertyKey, descriptor) { @@ -85,6 +102,9 @@ function getParent(type) { } return null; } +function throwInternalErrorInvalidType(expectedType, providedType) { + throw new InternalError(`Expecting ${expectedType}, received ${providedType}`, ValidationErrorType.InvalidType); +} function validateInput(input, expectedType, arrayType = null) { const parent = getParent(expectedType); if (parent) { @@ -107,7 +127,7 @@ function validateInput(input, expectedType, arrayType = null) { expectedType[key] = null; } else if (checkParams.onFailure !== 'ignore') { - const error = new InternalError('Field can\'t be null'); + const error = new InternalError('Field can\'t be null', ValidationErrorType.NullValue); error.fields.push(key); throw error; } @@ -118,7 +138,7 @@ function validateInput(input, expectedType, arrayType = null) { expectedType[key] = null; } else if (checkParams.onFailure !== 'ignore') { - const error = new InternalError('Field is required'); + const error = new InternalError('Field is required', ValidationErrorType.MissingField); error.fields.push(key); throw error; } @@ -145,7 +165,7 @@ function validateInput(input, expectedType, arrayType = null) { } } else { - throw new InternalError(`Expecting an instance of ${constructorName}, received ${JSON.stringify(input)}`); + throwInternalErrorInvalidType(`an instance of ${constructorName}`, JSON.stringify(input)); } return expectedType; } @@ -154,7 +174,7 @@ function validateInput(input, expectedType, arrayType = null) { const providedType = typeof input; if (constructorName === 'Array') { if (!Array.isArray(input)) { - throw new InternalError(`Expecting array, received ${providedType} ${JSON.stringify(input)}`); + throwInternalErrorInvalidType('array', `${providedType} ${JSON.stringify(input)}`); } for (const [index, item] of input.entries()) { try { @@ -169,13 +189,13 @@ function validateInput(input, expectedType, arrayType = null) { } else if (constructorName === 'Date') { if (input instanceof Date !== true || typeof input.getTime !== 'function' || isNaN(input.getTime())) { - throw new InternalError(`Expecting date, received ${providedType} ${JSON.stringify(input)}`); + throwInternalErrorInvalidType('date', `${providedType} ${JSON.stringify(input)}`); } } else { expectedType = typeof expectedType.valueOf(); if (providedType !== expectedType) { - throw new InternalError(`Expecting ${expectedType}, received ${providedType} ${JSON.stringify(input)}`); + throwInternalErrorInvalidType(expectedType, `${providedType} ${JSON.stringify(input)}`); } } } @@ -186,12 +206,9 @@ function validate(input, expectedType, arrayType = null) { try { return validateInput(input, expectedType, arrayType); } - catch (e) { - const fields = e.fields; - if (fields.length > 0) { - throw new ValidationError(`${fields.join('.')}: ${e.message}`); - } - throw new ValidationError(e.message); + catch (err) { + const e = err; + throw new ValidationError(e.message, e.errorType, e.fields); } } exports.validate = validate; diff --git a/js/TypeChecker.js.map b/js/TypeChecker.js.map index 5c78601..3cda278 100644 --- a/js/TypeChecker.js.map +++ b/js/TypeChecker.js.map @@ -1 +1 @@ -{"version":3,"file":"TypeChecker.js","sourceRoot":"","sources":["../src/TypeChecker.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"TypeChecker.js","sourceRoot":"","sources":["../src/TypeChecker.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/package.json b/package.json index 37132a1..4930941 100644 --- a/package.json +++ b/package.json @@ -52,5 +52,5 @@ "tslint-microsoft-contrib": "^5.2.1", "typescript": "^3.9.10" }, - "typings": "index.d.ts" + "typings": "index.ts" } diff --git a/src/TypeChecker.ts b/src/TypeChecker.ts index ea4bac4..a309b28 100644 --- a/src/TypeChecker.ts +++ b/src/TypeChecker.ts @@ -13,11 +13,37 @@ export interface PropertyCheckParams { onFailure?: onFailure; } -export class ValidationError extends Error {} +export class ValidationError extends Error { + public readonly field: string | undefined; + public readonly errorType: ValidationErrorType; + + constructor(message: string, errorType: ValidationErrorType, fields?: string[]) { + if (Array.isArray(fields) && fields.length > 0) { + super(`${fields.join('.')}: ${message}`); + this.field = fields[fields.length - 1]; + } else { + super(message); + } + + this.errorType = errorType; + } +} + +export enum ValidationErrorType { + NullValue = 'null', + MissingField = 'missing', + InvalidType = 'invalid' +} class InternalError extends Error { public fields: string[] = []; + public errorType: ValidationErrorType; public index?: number; + + constructor(message: string, errorType: ValidationErrorType) { + super(message); + this.errorType = errorType; + } } export function TypesCheck(target: any, propertyKey: string, descriptor: TypedPropertyDescriptor): any { @@ -107,6 +133,12 @@ function getParent(type: any): any { return null; } +function throwInternalErrorInvalidType(expectedType: string, providedType: string): never { + throw new InternalError( + `Expecting ${expectedType}, received ${providedType}`, ValidationErrorType.InvalidType + ); +} + function validateInput(input: any, expectedType: any, arrayType: any = null): any { // Try to validate properties from parent class if existing const parent = getParent(expectedType); @@ -133,7 +165,7 @@ function validateInput(input: any, expectedType: any, arrayType: any = null): an if (checkParams.onFailure === 'setNull') { expectedType[key] = null; } else if (checkParams.onFailure !== 'ignore') { - const error = new InternalError('Field can\'t be null'); + const error = new InternalError('Field can\'t be null', ValidationErrorType.NullValue); error.fields.push(key); throw error; } @@ -142,7 +174,7 @@ function validateInput(input: any, expectedType: any, arrayType: any = null): an if (checkParams.onFailure === 'setNull') { expectedType[key] = null; } else if (checkParams.onFailure !== 'ignore') { - const error = new InternalError('Field is required'); + const error = new InternalError('Field is required', ValidationErrorType.MissingField); error.fields.push(key); throw error; } @@ -169,7 +201,7 @@ function validateInput(input: any, expectedType: any, arrayType: any = null): an expectedType[uncheckedKey] = input[uncheckedKey]; } } else { - throw new InternalError(`Expecting an instance of ${constructorName}, received ${JSON.stringify(input)}`); + throwInternalErrorInvalidType(`an instance of ${constructorName}`, JSON.stringify(input)); } return expectedType; } else { // Else it's a basic type or a complex type with no validation @@ -177,7 +209,7 @@ function validateInput(input: any, expectedType: any, arrayType: any = null): an const providedType = typeof input; if (constructorName === 'Array') { if (!Array.isArray(input)) { - throw new InternalError(`Expecting array, received ${providedType} ${JSON.stringify(input)}`); + throwInternalErrorInvalidType('array', `${providedType} ${JSON.stringify(input)}`); } for (const [index, item] of input.entries()) { @@ -192,12 +224,12 @@ function validateInput(input: any, expectedType: any, arrayType: any = null): an return expectedType; } else if (constructorName === 'Date') { if (input instanceof Date !== true || typeof input.getTime !== 'function' || isNaN(input.getTime())) { - throw new InternalError(`Expecting date, received ${providedType} ${JSON.stringify(input)}`); + throwInternalErrorInvalidType('date', `${providedType} ${JSON.stringify(input)}`); } } else { expectedType = typeof expectedType.valueOf(); if (providedType !== expectedType) { - throw new InternalError(`Expecting ${expectedType}, received ${providedType} ${JSON.stringify(input)}`); + throwInternalErrorInvalidType(expectedType, `${providedType} ${JSON.stringify(input)}`); } } } @@ -210,11 +242,8 @@ export function validate(input: any, expectedType export function validate(input: any, expectedType: any, arrayType: any = null): any { try { return validateInput(input, expectedType, arrayType); - } catch (e) { - const fields = ( e).fields; - if (fields.length > 0) { - throw new ValidationError(`${fields.join('.')}: ${e.message}`); - } - throw new ValidationError(e.message); + } catch (err) { + const e = err; + throw new ValidationError(e.message, e.errorType, e.fields); } } diff --git a/tests/TypeChecker.ts b/tests/TypeChecker.ts index 68843dd..f3135e9 100644 --- a/tests/TypeChecker.ts +++ b/tests/TypeChecker.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; import { validate, PropertyCheck, TypesCheck, TypeCheck } from '../src/TypeChecker'; +import { ValidationError, ValidationErrorType } from '..'; class Bar { @PropertyCheck() @@ -180,6 +181,27 @@ describe('TypeChecker', () => { }, 'Result should be as expected'); expect(result).to.be.instanceof(Z); }); + + it('should return the validation error type in the thrown error for basic types', () => { + let error: ValidationError | undefined; + try { + validate(new Date('foo'), Date); + } catch (err) { + error = err; + } + expect(error.errorType).to.equal(ValidationErrorType.InvalidType); + }); + + it('should return the validation error type and field name in the thrown error for complex objects', () => { + let error: ValidationError | undefined; + try { + validate(new Foo(), Foo); + } catch (err) { + error = err; + } + expect(error.errorType).to.equal(ValidationErrorType.MissingField); + expect(error.field).to.equal('name'); + }); }); describe('TypesCheck()', () => {