From 47f04a91e3e2b08772e27f975d17fdc9d9c5dfd3 Mon Sep 17 00:00:00 2001 From: oberbeck <20164654+oberbeck@users.noreply.github.com> Date: Tue, 19 Nov 2024 18:47:05 +0100 Subject: [PATCH] feat(guard) reintroduce check as guard discussion: unlike check, guard is only visible when Output extend Input --- deno/lib/__tests__/guard.test-d.ts | 29 +++++++++++++++++++++++++++++ deno/lib/types.ts | 7 +++++++ src/__tests__/guard.test-d.ts | 29 +++++++++++++++++++++++++++++ src/types.ts | 7 +++++++ 4 files changed, 72 insertions(+) create mode 100644 deno/lib/__tests__/guard.test-d.ts create mode 100644 src/__tests__/guard.test-d.ts diff --git a/deno/lib/__tests__/guard.test-d.ts b/deno/lib/__tests__/guard.test-d.ts new file mode 100644 index 000000000..bf1423487 --- /dev/null +++ b/deno/lib/__tests__/guard.test-d.ts @@ -0,0 +1,29 @@ +import { describe, expectTypeOf, test } from "vitest"; +import { z } from "../index.ts"; + +describe("guard", () => { + test("work as a type guard when Output extends Input", () => { + expectTypeOf().toEqualTypeOf(); + }); + test("work as a type guard when Output *does* extend Input", () => { + const inputExtendsOutputSchema = z.object({ + a: z.string(), + b: z.enum(["x", "y"]).transform((arg) => arg as string), + }); + const val = null as unknown; + if (inputExtendsOutputSchema.guard(val)) { + expectTypeOf(val).toEqualTypeOf<{ a: string; b: string }>(); + } else { + expectTypeOf(val).toEqualTypeOf(); + } + }); + + test("to be unavailable when Output *does not* extend Input", () => { + const inputDiffersFromOutputSchema = z + .string() + .transform((arg) => parseFloat(arg)); + const val = null as unknown; + // @ts-expect-error - compile error as Input does not extend Output + inputDiffersFromOutputSchema.guard(val); + }); +}); diff --git a/deno/lib/types.ts b/deno/lib/types.ts index 5d020d278..5905ec06f 100644 --- a/deno/lib/types.ts +++ b/deno/lib/types.ts @@ -517,6 +517,13 @@ export abstract class ZodType< isNullable(): boolean { return this.safeParse(null).success; } + + guard( + this: ZodType, + data: unknown + ): data is Output { + return this.safeParse(data).success; + } } ///////////////////////////////////////// diff --git a/src/__tests__/guard.test-d.ts b/src/__tests__/guard.test-d.ts new file mode 100644 index 000000000..bcbc6b63e --- /dev/null +++ b/src/__tests__/guard.test-d.ts @@ -0,0 +1,29 @@ +import { describe, expectTypeOf, test } from "vitest"; +import { z } from "../index"; + +describe("guard", () => { + test("work as a type guard when Output extends Input", () => { + expectTypeOf().toEqualTypeOf(); + }); + test("work as a type guard when Output *does* extend Input", () => { + const inputExtendsOutputSchema = z.object({ + a: z.string(), + b: z.enum(["x", "y"]).transform((arg) => arg as string), + }); + const val = null as unknown; + if (inputExtendsOutputSchema.guard(val)) { + expectTypeOf(val).toEqualTypeOf<{ a: string; b: string }>(); + } else { + expectTypeOf(val).toEqualTypeOf(); + } + }); + + test("to be unavailable when Output *does not* extend Input", () => { + const inputDiffersFromOutputSchema = z + .string() + .transform((arg) => parseFloat(arg)); + const val = null as unknown; + // @ts-expect-error - compile error as Input does not extend Output + inputDiffersFromOutputSchema.guard(val); + }); +}); diff --git a/src/types.ts b/src/types.ts index f3730ae14..a0ec8bebf 100644 --- a/src/types.ts +++ b/src/types.ts @@ -517,6 +517,13 @@ export abstract class ZodType< isNullable(): boolean { return this.safeParse(null).success; } + + guard( + this: ZodType, + data: unknown + ): data is Output { + return this.safeParse(data).success; + } } /////////////////////////////////////////