diff --git a/deno/lib/__tests__/json.test.ts b/deno/lib/__tests__/json.test.ts index e75673f49b..9682f3384b 100644 --- a/deno/lib/__tests__/json.test.ts +++ b/deno/lib/__tests__/json.test.ts @@ -7,6 +7,17 @@ import * as z from "../index.ts"; // @ts-ignore TS2304 const isDeno = typeof Deno === "object"; +test("overload types", () => { + const schema = z.string().json(); + z.util.assertEqual(true); + const schema2 = z.string().json(z.number()); + z.util.assertEqual< + typeof schema2, + z.ZodPipeline, z.ZodNumber> + >(true); + const r2 = schema2.parse("12"); + z.util.assertEqual(true); +}); test("parse string to json", async () => { const Env = z.object({ myJsonConfig: z.string().json(z.object({ foo: z.number() })), @@ -62,9 +73,7 @@ test("parse string to json", async () => { { code: "invalid_string", validation: "json", - message: isDeno - ? `Unexpected token 'T', "This is no"... is not valid JSON` - : "Unexpected token T in JSON at position 0", + message: "Invalid json", path: ["myJsonConfig"], }, { @@ -78,3 +87,16 @@ test("parse string to json", async () => { }, }); }); + +test("no argument", () => { + const schema = z.string().json(); + z.util.assertEqual(true); + z.string().json().parse(`{}`); + z.string().json().parse(`null`); + z.string().json().parse(`12`); + z.string().json().parse(`{ "test": "test"}`); + expect(() => z.string().json().parse(`asdf`)).toThrow(); + expect(() => z.string().json().parse(`{ "test": undefined }`)).toThrow(); + expect(() => z.string().json().parse(`{ "test": 12n }`)).toThrow(); + expect(() => z.string().json().parse(`{ test: "test" }`)).toThrow(); +}); diff --git a/deno/lib/types.ts b/deno/lib/types.ts index 7dd7c4b84b..8b356fd8dd 100644 --- a/deno/lib/types.ts +++ b/deno/lib/types.ts @@ -537,7 +537,8 @@ export type ZodStringCheck = precision: number | null; message?: string; } - | { kind: "ip"; version?: IpVersion; message?: string }; + | { kind: "ip"; version?: IpVersion; message?: string } + | { kind: "json"; message?: string }; export interface ZodStringDef extends ZodTypeDef { checks: ZodStringCheck[]; @@ -845,6 +846,18 @@ export class ZodString extends ZodType { }); status.dirty(); } + } else if (check.kind === "json") { + try { + JSON.parse(input.data); + } catch (err) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: "json", + message: check.message, + }); + status.dirty(); + } } else { util.assertNever(check); } @@ -957,19 +970,27 @@ export class ZodString extends ZodType { }); } - json(shape: T) { - return this.transform((val, ctx) => { + json(message?: errorUtil.ErrMessage): this; + json( + pipeTo: T + ): ZodPipeline>, T>; + json(input?: errorUtil.ErrMessage | ZodTypeAny) { + if (!(input instanceof ZodType)) { + return this._addCheck({ kind: "json", ...errorUtil.errToObj(input) }); + } + const schema = this.transform((val, ctx) => { try { return JSON.parse(val); } catch (error: unknown) { ctx.addIssue({ code: ZodIssueCode.invalid_string, validation: "json", - message: (error as Error).message, + // message: (error as Error).message, }); return NEVER; } - }).pipe(shape); + }); + return input ? schema.pipe(input) : schema; } min(minLength: number, message?: errorUtil.ErrMessage) { diff --git a/playground.ts b/playground.ts index aa2c57f27d..394760b630 100644 --- a/playground.ts +++ b/playground.ts @@ -1,32 +1,5 @@ import { z } from "./src"; -import * as mod from "node:module"; z; -// @ts-ignore -// const arg = await import("./index"); -// arg; -// require.resolve(arg); - -load: { -} -interface A { - name: T; - children: any; - render(): ReadableStream | string; -} -function Hyper(arg: T) { - return function () {} as any as { - prototype: A; - new (): A; - }; -} - -export default class extends Hyper({ name: 1234 }) { - render() { - // this.name.name; - return `asdf ${this.children} asdf`; - } -} - -const a = new Arg(); -a.name; +const r = z.string().json().safeParse("asf"); +console.log(JSON.stringify(r, null, 2)); diff --git a/src/__tests__/json.test.ts b/src/__tests__/json.test.ts index fb14252c2a..067c25f8b4 100644 --- a/src/__tests__/json.test.ts +++ b/src/__tests__/json.test.ts @@ -6,6 +6,17 @@ import * as z from "../index"; // @ts-ignore TS2304 const isDeno = typeof Deno === "object"; +test("overload types", () => { + const schema = z.string().json(); + z.util.assertEqual(true); + const schema2 = z.string().json(z.number()); + z.util.assertEqual< + typeof schema2, + z.ZodPipeline, z.ZodNumber> + >(true); + const r2 = schema2.parse("12"); + z.util.assertEqual(true); +}); test("parse string to json", async () => { const Env = z.object({ myJsonConfig: z.string().json(z.object({ foo: z.number() })), @@ -61,9 +72,7 @@ test("parse string to json", async () => { { code: "invalid_string", validation: "json", - message: isDeno - ? `Unexpected token 'T', "This is no"... is not valid JSON` - : "Unexpected token T in JSON at position 0", + message: "Invalid json", path: ["myJsonConfig"], }, { @@ -77,3 +86,16 @@ test("parse string to json", async () => { }, }); }); + +test("no argument", () => { + const schema = z.string().json(); + z.util.assertEqual(true); + z.string().json().parse(`{}`); + z.string().json().parse(`null`); + z.string().json().parse(`12`); + z.string().json().parse(`{ "test": "test"}`); + expect(() => z.string().json().parse(`asdf`)).toThrow(); + expect(() => z.string().json().parse(`{ "test": undefined }`)).toThrow(); + expect(() => z.string().json().parse(`{ "test": 12n }`)).toThrow(); + expect(() => z.string().json().parse(`{ test: "test" }`)).toThrow(); +}); diff --git a/src/types.ts b/src/types.ts index 3c457432d3..0ea302bf48 100644 --- a/src/types.ts +++ b/src/types.ts @@ -537,7 +537,8 @@ export type ZodStringCheck = precision: number | null; message?: string; } - | { kind: "ip"; version?: IpVersion; message?: string }; + | { kind: "ip"; version?: IpVersion; message?: string } + | { kind: "json"; message?: string }; export interface ZodStringDef extends ZodTypeDef { checks: ZodStringCheck[]; @@ -845,6 +846,18 @@ export class ZodString extends ZodType { }); status.dirty(); } + } else if (check.kind === "json") { + try { + JSON.parse(input.data); + } catch (err) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: "json", + message: check.message, + }); + status.dirty(); + } } else { util.assertNever(check); } @@ -957,19 +970,27 @@ export class ZodString extends ZodType { }); } - json(shape: T) { - return this.transform((val, ctx) => { + json(message?: errorUtil.ErrMessage): this; + json( + pipeTo: T + ): ZodPipeline>, T>; + json(input?: errorUtil.ErrMessage | ZodTypeAny) { + if (!(input instanceof ZodType)) { + return this._addCheck({ kind: "json", ...errorUtil.errToObj(input) }); + } + const schema = this.transform((val, ctx) => { try { return JSON.parse(val); } catch (error: unknown) { ctx.addIssue({ code: ZodIssueCode.invalid_string, validation: "json", - message: (error as Error).message, + // message: (error as Error).message, }); return NEVER; } - }).pipe(shape); + }); + return input ? schema.pipe(input) : schema; } min(minLength: number, message?: errorUtil.ErrMessage) {