diff --git a/example/index.ts b/example/index.ts index cbbe2c7d..09e32442 100644 --- a/example/index.ts +++ b/example/index.ts @@ -7,9 +7,9 @@ const UnsafeByte = Type.Unsafe({ type: 'byte' }) const Byte = Type.Refine(UnsafeByte) .Check((value) => typeof value === 'number') - .Check((value) => !isNaN(value)) - .Check((value) => value >= 0) - .Check((value) => value < 256) + .Check((value) => !isNaN(value), { message: 'Must not be NaN', x: 100 }) + .Check((value) => value >= 0, { message: 'Must be greater than 0' }) + .Check((value) => value < 256, { message: 'Must be something' }) .Done() const A = Type.Object({ @@ -18,12 +18,9 @@ const A = Type.Object({ z: Byte, }) -console.log(Byte) +console.dir(A, { depth: 100 }) console.log(TypeCompiler.Code(A)) -console.log(Value.Check(A, { x: 0, y: 0, z: 0 })) -console.log(Value.Check(Byte, 255)) -console.log(Value.Check(Byte, -1)) -console.log(Value.Check(Byte, NaN)) +console.log(Value.Errors(Byte, 'asdsa').Take(10)) // Todo: Error Tests // Todo: Investigate Error Propogation for Refinements diff --git a/src/errors/errors.ts b/src/errors/errors.ts index 5940fdad..0f55bd36 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -132,7 +132,7 @@ export enum ValueErrorType { Object, Promise, RegExp, - Refine, + Refinement, StringFormatNotFound, StringFormat, StringMaxLength, @@ -199,7 +199,7 @@ export class ValueErrorIterator { // Create // -------------------------------------------------------------------------- function CreateRefinementError(schema: TSchema, refinement: Refinement, path: string, value: unknown): ValueError { - return { type: ValueErrorType.Refine, schema, path, value, message: refinement.message } + return { type: ValueErrorType.Refinement, schema, path, value, message: refinement.message || '' } } function CreateError(errorType: ValueErrorType, schema: TSchema, path: string, value: unknown): ValueError { return { type: errorType, schema, path, value, message: GetErrorFunction()({ errorType, path, schema, value }) } @@ -541,7 +541,9 @@ function* FromKind(schema: TSchema, references: TSchema[], path: string, value: } function* FromRefine(schema: TSchema & Record & { [RefineKind]: Refinement[] }, references: TSchema[], path: string, value: any): IterableIterator { for (const refinement of schema[RefineKind]) { - if (!refinement.check(value)) yield CreateRefinementError(schema, refinement, path, value) + if (refinement.check(value)) continue + // only generate one refinement error per type + return yield CreateRefinementError(schema, refinement, path, value) } } function* Visit(schema: T, references: TSchema[], path: string, value: any): IterableIterator { diff --git a/src/errors/function.ts b/src/errors/function.ts index 66362c80..29fe3d57 100644 --- a/src/errors/function.ts +++ b/src/errors/function.ts @@ -32,7 +32,7 @@ import { IsRefinement } from '../type/guard/type' import { ValueErrorType } from './errors' /** Creates an error message using en-US as the default locale */ -export function DefaultErrorFunction(error: ErrorFunctionParameter) { +export function DefaultErrorFunction(error: ErrorFunctionParameter): string { switch (error.errorType) { case ValueErrorType.ArrayContains: return 'Expected array to contain at least one matching value' @@ -128,7 +128,7 @@ export function DefaultErrorFunction(error: ErrorFunctionParameter) { return 'Required property' case ValueErrorType.Promise: return 'Expected Promise' - case ValueErrorType.Refine: + case ValueErrorType.Refinement: return IsRefinement(error.schema) ? error.schema.message : 'Refinement Error' case ValueErrorType.RegExp: return 'Expected string to match regular expression' diff --git a/src/type/refine/refine.ts b/src/type/refine/refine.ts index 56115c9f..fc356d4f 100644 --- a/src/type/refine/refine.ts +++ b/src/type/refine/refine.ts @@ -32,20 +32,25 @@ import { Static } from '../static/index' import { CloneType } from '../clone/type' import { IsRefine } from '../guard/type' -export interface Refinement { - check: RefineCheckFunction +export interface RefinementCheckOptions { + [key: string]: any message: string } - +export interface Refinement extends RefinementCheckOptions { + check: RefineCheckFunction +} export type RefineCheckFunction> = (value: S) => boolean +function DefaultRefinementCheckOptions(): RefinementCheckOptions { + return { message: 'Invalid' } +} export class RefineBuilder { constructor(private readonly schema: T, private readonly refinements: Refinement[]) {} /** Adds a refinement check to this type */ - public Check(check: RefineCheckFunction, message: string = ''): RefineBuilder { - return new RefineBuilder(this.schema, [...this.refinements, { check, message }]) + public Check(check: RefineCheckFunction, options: RefinementCheckOptions = DefaultRefinementCheckOptions()): RefineBuilder { + return new RefineBuilder(this.schema, [...this.refinements, { check, ...options }]) } - /** Applies refinement checks and returns the type */ + /** Completes the refinement and returns the type. */ public Done(): T { return CloneType(this.schema, { [RefineKind]: [...this.refinements] }) }