diff --git a/src/errors/errors.ts b/src/errors/errors.ts index edb58a06d..44a8b0227 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -34,6 +34,7 @@ import { GetErrorFunction } from './function' import { TypeBoxError } from '../type/error/index' import { Deref } from '../value/deref/index' import { Hash } from '../value/hash/index' +import { Check } from '../value/check/index' import { Kind } from '../type/symbols/index' import type { TSchema } from '../type/schema/index' @@ -167,6 +168,7 @@ export interface ValueError { path: string value: unknown message: string + errors: ValueErrorIterator[] } // ------------------------------------------------------------------ // ValueErrors @@ -205,8 +207,15 @@ export class ValueErrorIterator { // -------------------------------------------------------------------------- // Create // -------------------------------------------------------------------------- -function Create(errorType: ValueErrorType, schema: TSchema, path: string, value: unknown): ValueError { - return { type: errorType, schema, path, value, message: GetErrorFunction()({ errorType, path, schema, value }) } +function Create(errorType: ValueErrorType, schema: TSchema, path: string, value: unknown, errors: ValueErrorIterator[] = []): ValueError { + return { + type: errorType, + schema, + path, + value, + message: GetErrorFunction()({ errorType, path, schema, value, errors }), + errors, + } } // -------------------------------------------------------------------------- // Types @@ -516,15 +525,9 @@ function* FromUndefined(schema: TUndefined, references: TSchema[], path: string, if (!IsUndefined(value)) yield Create(ValueErrorType.Undefined, schema, path, value) } function* FromUnion(schema: TUnion, references: TSchema[], path: string, value: any): IterableIterator { - let count = 0 - for (const subschema of schema.anyOf) { - const errors = [...Visit(subschema, references, path, value)] - if (errors.length === 0) return // matched - count += errors.length - } - if (count > 0) { - yield Create(ValueErrorType.Union, schema, path, value) - } + if (Check(schema, references, value)) return + const errors = schema.anyOf.map((variant) => new ValueErrorIterator(Visit(variant, references, path, value))) + yield Create(ValueErrorType.Union, schema, path, value, errors) } function* FromUint8Array(schema: TUint8Array, references: TSchema[], path: string, value: any): IterableIterator { if (!IsUint8Array(value)) return yield Create(ValueErrorType.Uint8Array, schema, path, value) diff --git a/src/errors/function.ts b/src/errors/function.ts index 94504c5ad..536f3e682 100644 --- a/src/errors/function.ts +++ b/src/errors/function.ts @@ -28,7 +28,7 @@ THE SOFTWARE. import { TSchema } from '../type/schema/index' import { Kind } from '../type/symbols/index' -import { ValueErrorType } from './errors' +import { ValueErrorIterator, ValueErrorType } from './errors' /** Creates an error message using en-US as the default locale */ export function DefaultErrorFunction(error: ErrorFunctionParameter) { @@ -178,6 +178,8 @@ export type ErrorFunctionParameter = { schema: TSchema /** The value associated with the error */ value: unknown + /** Interior errors for this error */ + errors: ValueErrorIterator[] } export type ErrorFunction = (parameter: ErrorFunctionParameter) => string /** Manages error message providers */ diff --git a/test/runtime/errors/types/union.ts b/test/runtime/errors/types/union.ts index df110ed30..70d1389a3 100644 --- a/test/runtime/errors/types/union.ts +++ b/test/runtime/errors/types/union.ts @@ -1,5 +1,5 @@ import { Type } from '@sinclair/typebox' -import { ValueErrorType } from '@sinclair/typebox/errors' +import { ValueErrorIterator, ValueErrorType } from '@sinclair/typebox/errors' import { Resolve } from './resolve' import { Assert } from '../../assert' @@ -17,5 +17,7 @@ describe('errors/type/Union', () => { const R = Resolve(T, true) Assert.IsEqual(R.length, 1) Assert.IsEqual(R[0].type, ValueErrorType.Union) + Assert.IsEqual(R[0].errors[0].First()?.type, ValueErrorType.String) + Assert.IsEqual(R[0].errors[1].First()?.type, ValueErrorType.Number) }) })