Skip to content

Commit 16beeb5

Browse files
lowercase method for ZodString (#2038)
* lowercase method for ZodString * Rename to toLowerCase. Add toUpperCase.: * Update readme --------- Co-authored-by: Colin McDonnell <[email protected]>
1 parent 8de36eb commit 16beeb5

File tree

6 files changed

+53
-3
lines changed

6 files changed

+53
-3
lines changed

Diff for: README.md

+2
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,8 @@ z.string().regex(regex);
596596
z.string().startsWith(string);
597597
z.string().endsWith(string);
598598
z.string().trim(); // trim whitespace
599+
z.string().toLowerCase(); // toLowerCase
600+
z.string().toUpperCase(); // toLowerCase
599601
z.string().datetime(); // defaults to UTC, see below for options
600602
z.string().ip(); // defaults to IPv4 and IPv6, see below for options
601603
```

Diff for: deno/lib/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,8 @@ z.string().regex(regex);
596596
z.string().startsWith(string);
597597
z.string().endsWith(string);
598598
z.string().trim(); // trim whitespace
599+
z.string().toLowerCase(); // toLowerCase
600+
z.string().toUpperCase(); // toLowerCase
599601
z.string().datetime(); // defaults to UTC, see below for options
600602
z.string().ip(); // defaults to IPv4 and IPv6, see below for options
601603
```

Diff for: deno/lib/__tests__/string.test.ts

+5
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,11 @@ test("trim", () => {
309309
expect(() => z.string().trim().min(2).parse(" 1 ")).toThrow();
310310
});
311311

312+
test("lowerCase", () => {
313+
expect(z.string().toLowerCase().parse("ASDF")).toEqual("asdf");
314+
expect(z.string().toUpperCase().parse("asdf")).toEqual("ASDF");
315+
});
316+
312317
test("datetime", () => {
313318
const a = z.string().datetime({});
314319
expect(a.isDatetime).toEqual(true);

Diff for: deno/lib/types.ts

+19-2
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,8 @@ export type ZodStringCheck =
501501
| { kind: "endsWith"; value: string; message?: string }
502502
| { kind: "regex"; regex: RegExp; message?: string }
503503
| { kind: "trim"; message?: string }
504+
| { kind: "toLowerCase"; message?: string }
505+
| { kind: "toUpperCase"; message?: string }
504506
| {
505507
kind: "datetime";
506508
offset: boolean;
@@ -529,15 +531,14 @@ const uuidRegex =
529531
const emailRegex =
530532
/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\])|(\[IPv6:(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))\])|([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])*(\.[A-Za-z]{2,})+))$/;
531533
// from https://thekevinscott.com/emojis-in-javascript/#writing-a-regular-expression
534+
const emojiRegex = /^(\p{Extended_Pictographic}|\p{Emoji_Component})+$/u;
532535

533536
const ipv4Regex =
534537
/^(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))$/;
535538

536539
const ipv6Regex =
537540
/^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/;
538541

539-
const emojiRegex = /^(\p{Extended_Pictographic}|\p{Emoji_Component})+$/u;
540-
541542
// Adapted from https://stackoverflow.com/a/3143231
542543
const datetimeRegex = (args: { precision: number | null; offset: boolean }) => {
543544
if (args.precision) {
@@ -735,6 +736,10 @@ export class ZodString extends ZodType<string, ZodStringDef> {
735736
}
736737
} else if (check.kind === "trim") {
737738
input.data = input.data.trim();
739+
} else if (check.kind === "toLowerCase") {
740+
input.data = input.data.toLowerCase();
741+
} else if (check.kind === "toUpperCase") {
742+
input.data = input.data.toUpperCase();
738743
} else if (check.kind === "startsWith") {
739744
if (!(input.data as string).startsWith(check.value)) {
740745
ctx = this._getOrReturnCtx(input, ctx);
@@ -913,6 +918,18 @@ export class ZodString extends ZodType<string, ZodStringDef> {
913918
checks: [...this._def.checks, { kind: "trim" }],
914919
});
915920

921+
toLowerCase = () =>
922+
new ZodString({
923+
...this._def,
924+
checks: [...this._def.checks, { kind: "toLowerCase" }],
925+
});
926+
927+
toUpperCase = () =>
928+
new ZodString({
929+
...this._def,
930+
checks: [...this._def.checks, { kind: "toUpperCase" }],
931+
});
932+
916933
get isDatetime() {
917934
return !!this._def.checks.find((ch) => ch.kind === "datetime");
918935
}

Diff for: src/__tests__/string.test.ts

+5
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,11 @@ test("trim", () => {
308308
expect(() => z.string().trim().min(2).parse(" 1 ")).toThrow();
309309
});
310310

311+
test("lowerCase", () => {
312+
expect(z.string().toLowerCase().parse("ASDF")).toEqual("asdf");
313+
expect(z.string().toUpperCase().parse("asdf")).toEqual("ASDF");
314+
});
315+
311316
test("datetime", () => {
312317
const a = z.string().datetime({});
313318
expect(a.isDatetime).toEqual(true);

Diff for: src/types.ts

+20-1
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,8 @@ export type ZodStringCheck =
501501
| { kind: "endsWith"; value: string; message?: string }
502502
| { kind: "regex"; regex: RegExp; message?: string }
503503
| { kind: "trim"; message?: string }
504+
| { kind: "toLowerCase"; message?: string }
505+
| { kind: "toUpperCase"; message?: string }
504506
| {
505507
kind: "datetime";
506508
offset: boolean;
@@ -526,7 +528,8 @@ const uuidRegex =
526528
// const emailRegex = /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@((?!-)([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{1,})[^-<>()[\].,;:\s@"]$/i;
527529
// eslint-disable-next-line
528530

529-
const emailRegex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\])|(\[IPv6:(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))\])|([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])*(\.[A-Za-z]{2,})+))$/;
531+
const emailRegex =
532+
/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\])|(\[IPv6:(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))\])|([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])*(\.[A-Za-z]{2,})+))$/;
530533
// from https://thekevinscott.com/emojis-in-javascript/#writing-a-regular-expression
531534
const emojiRegex = /^(\p{Extended_Pictographic}|\p{Emoji_Component})+$/u;
532535

@@ -733,6 +736,10 @@ export class ZodString extends ZodType<string, ZodStringDef> {
733736
}
734737
} else if (check.kind === "trim") {
735738
input.data = input.data.trim();
739+
} else if (check.kind === "toLowerCase") {
740+
input.data = input.data.toLowerCase();
741+
} else if (check.kind === "toUpperCase") {
742+
input.data = input.data.toUpperCase();
736743
} else if (check.kind === "startsWith") {
737744
if (!(input.data as string).startsWith(check.value)) {
738745
ctx = this._getOrReturnCtx(input, ctx);
@@ -911,6 +918,18 @@ export class ZodString extends ZodType<string, ZodStringDef> {
911918
checks: [...this._def.checks, { kind: "trim" }],
912919
});
913920

921+
toLowerCase = () =>
922+
new ZodString({
923+
...this._def,
924+
checks: [...this._def.checks, { kind: "toLowerCase" }],
925+
});
926+
927+
toUpperCase = () =>
928+
new ZodString({
929+
...this._def,
930+
checks: [...this._def.checks, { kind: "toUpperCase" }],
931+
});
932+
914933
get isDatetime() {
915934
return !!this._def.checks.find((ch) => ch.kind === "datetime");
916935
}

0 commit comments

Comments
 (0)