Skip to content

Commit

Permalink
Add exact flag to too_big and too_small
Browse files Browse the repository at this point in the history
  • Loading branch information
Colin McDonnell committed Dec 12, 2022
1 parent b16fa44 commit 36396bb
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 44 deletions.
2 changes: 2 additions & 0 deletions deno/lib/ZodError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,15 @@ export interface ZodTooSmallIssue extends ZodIssueBase {
code: typeof ZodIssueCode.too_small;
minimum: number;
inclusive: boolean;
exact: boolean;
type: "array" | "string" | "number" | "set" | "date";
}

export interface ZodTooBigIssue extends ZodIssueBase {
code: typeof ZodIssueCode.too_big;
maximum: number;
inclusive: boolean;
exact: boolean;
type: "array" | "string" | "number" | "set" | "date";
}

Expand Down
44 changes: 30 additions & 14 deletions deno/lib/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,39 +63,55 @@ const errorMap: ZodErrorMap = (issue, _ctx) => {
case ZodIssueCode.too_small:
if (issue.type === "array")
message = `Array must contain ${
issue.inclusive ? `at least` : `more than`
issue.exact ? "exactly" : issue.inclusive ? `at least` : `more than`
} ${issue.minimum} element(s)`;
else if (issue.type === "string")
message = `String must contain ${
issue.inclusive ? `at least` : `over`
issue.exact ? "exactly" : issue.inclusive ? `at least` : `over`
} ${issue.minimum} character(s)`;
else if (issue.type === "number")
message = `Number must be greater than ${
issue.inclusive ? `or equal to ` : ``
message = `Number must be ${
issue.exact
? `exactly equal to `
: issue.inclusive
? `greater than or equal to `
: `greater than `
}${issue.minimum}`;
else if (issue.type === "date")
message = `Date must be greater than ${
issue.inclusive ? `or equal to ` : ``
message = `Date must be ${
issue.exact
? `exactly equal to `
: issue.inclusive
? `greater than or equal to `
: `greater than `
}${new Date(issue.minimum)}`;
else message = "Invalid input";
break;
case ZodIssueCode.too_big:
if (issue.type === "array")
message = `Array must contain ${
issue.inclusive ? `at most` : `less than`
issue.exact ? `exactly` : issue.inclusive ? `at most` : `less than`
} ${issue.maximum} element(s)`;
else if (issue.type === "string")
message = `String must contain ${
issue.inclusive ? `at most` : `under`
issue.exact ? `exactly` : issue.inclusive ? `at most` : `under`
} ${issue.maximum} character(s)`;
else if (issue.type === "number")
message = `Number must be less than ${
issue.inclusive ? `or equal to ` : ``
}${issue.maximum}`;
message = `Number must be ${
issue.exact
? `exactly`
: issue.inclusive
? `less than or equal to`
: `less than`
} ${issue.maximum}`;
else if (issue.type === "date")
message = `Date must be smaller than ${
issue.inclusive ? `or equal to ` : ``
}${new Date(issue.maximum)}`;
message = `Date must be ${
issue.exact
? `exactly`
: issue.inclusive
? `smaller than or equal to`
: `smaller than`
} ${new Date(issue.maximum)}`;
else message = "Invalid input";
break;
case ZodIssueCode.custom:
Expand Down
75 changes: 67 additions & 8 deletions deno/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ export abstract class ZodType<
type ZodStringCheck =
| { kind: "min"; value: number; message?: string }
| { kind: "max"; value: number; message?: string }
| { kind: "length"; value: number; message?: string }
| { kind: "email"; message?: string }
| { kind: "url"; message?: string }
| { kind: "uuid"; message?: string }
Expand Down Expand Up @@ -572,6 +573,7 @@ export class ZodString extends ZodType<string, ZodStringDef> {
minimum: check.value,
type: "string",
inclusive: true,
exact: false,
message: check.message,
});
status.dirty();
Expand All @@ -584,10 +586,37 @@ export class ZodString extends ZodType<string, ZodStringDef> {
maximum: check.value,
type: "string",
inclusive: true,
exact: false,
message: check.message,
});
status.dirty();
}
} else if (check.kind === "length") {
const tooBig = input.data.length > check.value;
const tooSmall = input.data.length < check.value;
if (tooBig || tooSmall) {
ctx = this._getOrReturnCtx(input, ctx);
if (tooBig) {
addIssueToContext(ctx, {
code: ZodIssueCode.too_big,
maximum: check.value,
type: "string",
inclusive: true,
exact: true,
message: check.message,
});
} else if (tooSmall) {
addIssueToContext(ctx, {
code: ZodIssueCode.too_small,
minimum: check.value,
type: "string",
inclusive: true,
exact: true,
message: check.message,
});
}
status.dirty();
}
} else if (check.kind === "email") {
if (!emailRegex.test(input.data)) {
ctx = this._getOrReturnCtx(input, ctx);
Expand Down Expand Up @@ -781,10 +810,11 @@ export class ZodString extends ZodType<string, ZodStringDef> {
}

length(len: number, message?: errorUtil.ErrMessage) {
const defaultMessage = `String must contain exactly ${len} character(s)`;
const actualMessage = message ?? defaultMessage;

return this.min(len, actualMessage).max(len, actualMessage);
return this._addCheck({
kind: "length",
value: len,
...errorUtil.errToObj(message),
});
}

/**
Expand Down Expand Up @@ -913,6 +943,7 @@ export class ZodNumber extends ZodType<number, ZodNumberDef> {
minimum: check.value,
type: "number",
inclusive: check.inclusive,
exact: false,
message: check.message,
});
status.dirty();
Expand All @@ -928,6 +959,7 @@ export class ZodNumber extends ZodType<number, ZodNumberDef> {
maximum: check.value,
type: "number",
inclusive: check.inclusive,
exact: false,
message: check.message,
});
status.dirty();
Expand Down Expand Up @@ -1214,6 +1246,7 @@ export class ZodDate extends ZodType<Date, ZodDateDef> {
code: ZodIssueCode.too_small,
message: check.message,
inclusive: true,
exact: false,
minimum: check.value,
type: "date",
});
Expand All @@ -1226,6 +1259,7 @@ export class ZodDate extends ZodType<Date, ZodDateDef> {
code: ZodIssueCode.too_big,
message: check.message,
inclusive: true,
exact: false,
maximum: check.value,
type: "date",
});
Expand Down Expand Up @@ -1524,6 +1558,7 @@ export interface ZodArrayDef<T extends ZodTypeAny = ZodTypeAny>
extends ZodTypeDef {
type: T;
typeName: ZodFirstPartyTypeKind.ZodArray;
exactLength: { value: number; message?: string } | null;
minLength: { value: number; message?: string } | null;
maxLength: { value: number; message?: string } | null;
}
Expand Down Expand Up @@ -1560,13 +1595,31 @@ export class ZodArray<
return INVALID;
}

if (def.exactLength !== null) {
const tooBig = ctx.data.length > def.exactLength.value;
const tooSmall = ctx.data.length < def.exactLength.value;
if (tooBig || tooSmall) {
addIssueToContext(ctx, {
code: tooBig ? ZodIssueCode.too_big : ZodIssueCode.too_small,
minimum: (tooSmall ? def.exactLength.value : undefined) as number,
maximum: (tooBig ? def.exactLength.value : undefined) as number,
type: "array",
inclusive: true,
exact: true,
message: def.exactLength.message,
});
status.dirty();
}
}

if (def.minLength !== null) {
if (ctx.data.length < def.minLength.value) {
addIssueToContext(ctx, {
code: ZodIssueCode.too_small,
minimum: def.minLength.value,
type: "array",
inclusive: true,
exact: false,
message: def.minLength.message,
});
status.dirty();
Expand All @@ -1580,6 +1633,7 @@ export class ZodArray<
maximum: def.maxLength.value,
type: "array",
inclusive: true,
exact: false,
message: def.maxLength.message,
});
status.dirty();
Expand Down Expand Up @@ -1626,10 +1680,10 @@ export class ZodArray<
}

length(len: number, message?: errorUtil.ErrMessage): this {
const defaultMessage = `Array must contain exactly ${len} element(s)`;
const actualMessage = message ?? defaultMessage;

return this.min(len, actualMessage).max(len, actualMessage) as any;
return new ZodArray({
...this._def,
exactLength: { value: len, message: errorUtil.toString(message) },
}) as any;
}

nonempty(message?: errorUtil.ErrMessage): ZodArray<T, "atleastone"> {
Expand All @@ -1644,6 +1698,7 @@ export class ZodArray<
type: schema,
minLength: null,
maxLength: null,
exactLength: null,
typeName: ZodFirstPartyTypeKind.ZodArray,
...processCreateParams(params),
});
Expand Down Expand Up @@ -2706,6 +2761,7 @@ export class ZodTuple<
code: ZodIssueCode.too_small,
minimum: this._def.items.length,
inclusive: true,
exact: false,
type: "array",
});

Expand All @@ -2719,6 +2775,7 @@ export class ZodTuple<
code: ZodIssueCode.too_big,
maximum: this._def.items.length,
inclusive: true,
exact: false,
type: "array",
});
status.dirty();
Expand Down Expand Up @@ -3017,6 +3074,7 @@ export class ZodSet<Value extends ZodTypeAny = ZodTypeAny> extends ZodType<
minimum: def.minSize.value,
type: "set",
inclusive: true,
exact: false,
message: def.minSize.message,
});
status.dirty();
Expand All @@ -3030,6 +3088,7 @@ export class ZodSet<Value extends ZodTypeAny = ZodTypeAny> extends ZodType<
maximum: def.maxSize.value,
type: "set",
inclusive: true,
exact: false,
message: def.maxSize.message,
});
status.dirty();
Expand Down
2 changes: 2 additions & 0 deletions src/ZodError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,15 @@ export interface ZodTooSmallIssue extends ZodIssueBase {
code: typeof ZodIssueCode.too_small;
minimum: number;
inclusive: boolean;
exact: boolean;
type: "array" | "string" | "number" | "set" | "date";
}

export interface ZodTooBigIssue extends ZodIssueBase {
code: typeof ZodIssueCode.too_big;
maximum: number;
inclusive: boolean;
exact: boolean;
type: "array" | "string" | "number" | "set" | "date";
}

Expand Down
44 changes: 30 additions & 14 deletions src/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,39 +63,55 @@ const errorMap: ZodErrorMap = (issue, _ctx) => {
case ZodIssueCode.too_small:
if (issue.type === "array")
message = `Array must contain ${
issue.inclusive ? `at least` : `more than`
issue.exact ? "exactly" : issue.inclusive ? `at least` : `more than`
} ${issue.minimum} element(s)`;
else if (issue.type === "string")
message = `String must contain ${
issue.inclusive ? `at least` : `over`
issue.exact ? "exactly" : issue.inclusive ? `at least` : `over`
} ${issue.minimum} character(s)`;
else if (issue.type === "number")
message = `Number must be greater than ${
issue.inclusive ? `or equal to ` : ``
message = `Number must be ${
issue.exact
? `exactly equal to `
: issue.inclusive
? `greater than or equal to `
: `greater than `
}${issue.minimum}`;
else if (issue.type === "date")
message = `Date must be greater than ${
issue.inclusive ? `or equal to ` : ``
message = `Date must be ${
issue.exact
? `exactly equal to `
: issue.inclusive
? `greater than or equal to `
: `greater than `
}${new Date(issue.minimum)}`;
else message = "Invalid input";
break;
case ZodIssueCode.too_big:
if (issue.type === "array")
message = `Array must contain ${
issue.inclusive ? `at most` : `less than`
issue.exact ? `exactly` : issue.inclusive ? `at most` : `less than`
} ${issue.maximum} element(s)`;
else if (issue.type === "string")
message = `String must contain ${
issue.inclusive ? `at most` : `under`
issue.exact ? `exactly` : issue.inclusive ? `at most` : `under`
} ${issue.maximum} character(s)`;
else if (issue.type === "number")
message = `Number must be less than ${
issue.inclusive ? `or equal to ` : ``
}${issue.maximum}`;
message = `Number must be ${
issue.exact
? `exactly`
: issue.inclusive
? `less than or equal to`
: `less than`
} ${issue.maximum}`;
else if (issue.type === "date")
message = `Date must be smaller than ${
issue.inclusive ? `or equal to ` : ``
}${new Date(issue.maximum)}`;
message = `Date must be ${
issue.exact
? `exactly`
: issue.inclusive
? `smaller than or equal to`
: `smaller than`
} ${new Date(issue.maximum)}`;
else message = "Invalid input";
break;
case ZodIssueCode.custom:
Expand Down
Loading

0 comments on commit 36396bb

Please sign in to comment.