diff --git a/coverage.svg b/coverage.svg index 16e49a97b..66a0c9f15 100644 --- a/coverage.svg +++ b/coverage.svg @@ -1 +1,5 @@ -Coverage: 96.16%Coverage96.16% \ No newline at end of file +<<<<<<< HEAD +Coverage: 96.16%Coverage96.16% +======= +Coverage: 96.1%Coverage96.1% +>>>>>>> 3b6802c (Fast unions) diff --git a/deno/lib/types.ts b/deno/lib/types.ts index caf9c014b..bec55020f 100644 --- a/deno/lib/types.ts +++ b/deno/lib/types.ts @@ -2,6 +2,7 @@ import { errorUtil } from "./helpers/errorUtil.ts"; import { addIssueToContext, AsyncParseReturnType, + DIRTY, getParsedType, INVALID, isAborted, @@ -1849,6 +1850,7 @@ export class ZodUnion extends ZodType< const unionErrors = results.map( (result) => new ZodError(result.ctx.issues) ); + addIssueToContext(ctx, { code: ZodIssueCode.invalid_union, unionErrors, @@ -1875,23 +1877,44 @@ export class ZodUnion extends ZodType< }) ).then(handleResults); } else { - const optionResults = options.map((option) => { + let dirty: undefined | { result: DIRTY; ctx: ParseContext } = + undefined; + const issues: ZodIssue[][] = []; + for (const option of options) { const childCtx: ParseContext = { ...ctx, issues: [], parent: null, }; - return { - result: option._parseSync({ - data: ctx.data, - path: ctx.path, - parent: childCtx, - }), - ctx: childCtx, - }; + const result = option._parseSync({ + data: ctx.data, + path: ctx.path, + parent: childCtx, + }); + + if (result.status === "valid") { + return result; + } else if (result.status === "dirty" && !dirty) { + dirty = { result, ctx: childCtx }; + } + + if (childCtx.issues.length) { + issues.push(childCtx.issues); + } + } + + if (dirty) { + ctx.issues.push(...dirty.ctx.issues); + return dirty.result; + } + + const unionErrors = issues.map((issues) => new ZodError(issues)); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union, + unionErrors, }); - return handleResults(optionResults); + return INVALID; } } diff --git a/src/types.ts b/src/types.ts index b3b7c1df2..29db38b2a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2,6 +2,7 @@ import { errorUtil } from "./helpers/errorUtil"; import { addIssueToContext, AsyncParseReturnType, + DIRTY, getParsedType, INVALID, isAborted, @@ -1849,6 +1850,7 @@ export class ZodUnion extends ZodType< const unionErrors = results.map( (result) => new ZodError(result.ctx.issues) ); + addIssueToContext(ctx, { code: ZodIssueCode.invalid_union, unionErrors, @@ -1875,23 +1877,44 @@ export class ZodUnion extends ZodType< }) ).then(handleResults); } else { - const optionResults = options.map((option) => { + let dirty: undefined | { result: DIRTY; ctx: ParseContext } = + undefined; + const issues: ZodIssue[][] = []; + for (const option of options) { const childCtx: ParseContext = { ...ctx, issues: [], parent: null, }; - return { - result: option._parseSync({ - data: ctx.data, - path: ctx.path, - parent: childCtx, - }), - ctx: childCtx, - }; + const result = option._parseSync({ + data: ctx.data, + path: ctx.path, + parent: childCtx, + }); + + if (result.status === "valid") { + return result; + } else if (result.status === "dirty" && !dirty) { + dirty = { result, ctx: childCtx }; + } + + if (childCtx.issues.length) { + issues.push(childCtx.issues); + } + } + + if (dirty) { + ctx.issues.push(...dirty.ctx.issues); + return dirty.result; + } + + const unionErrors = issues.map((issues) => new ZodError(issues)); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union, + unionErrors, }); - return handleResults(optionResults); + return INVALID; } }