From a59c38452ed970e129ee9057c4e6d003d4d017bb Mon Sep 17 00:00:00 2001 From: Colin McDonnell Date: Sun, 17 Jul 2022 22:35:34 -0700 Subject: [PATCH] Fix inferred function types --- .github/workflows/release.yml | 2 +- deno/lib/__tests__/function.test.ts | 47 +++++++++++++++++++++-------- deno/lib/helpers/util.ts | 1 + deno/lib/types.ts | 6 +++- package.json | 2 +- src/__tests__/function.test.ts | 47 +++++++++++++++++++++-------- src/helpers/util.ts | 1 + src/types.ts | 6 +++- 8 files changed, 84 insertions(+), 28 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 61168fe08..cd22148ce 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -111,7 +111,7 @@ jobs: with: tag_name: v${{ steps.publish.outputs.version }} release_name: v${{ steps.publish.outputs.version }} - commitish: ref/heads/main + commitish: refs/heads/main body: ${{steps.github_release.outputs.changelog}} draft: false prerelease: false diff --git a/deno/lib/__tests__/function.test.ts b/deno/lib/__tests__/function.test.ts index cb57613b9..46aa8aab8 100644 --- a/deno/lib/__tests__/function.test.ts +++ b/deno/lib/__tests__/function.test.ts @@ -26,26 +26,21 @@ test("parsed function fail 2", () => { test("function inference 1", () => { type func1 = z.TypeOf; - const t1: util.AssertEqual number> = true; - [t1]; + util.assertEqual number>(true); }); test("args method", () => { const t1 = z.function(); type t1 = z.infer; - const f1: util.AssertEqual void> = true; + util.assertEqual void>(true); const t2 = t1.args(z.string()); type t2 = z.infer; - const f2: util.AssertEqual void> = true; + util.assertEqual void>(true); const t3 = t2.returns(z.boolean()); type t3 = z.infer; - const f3: util.AssertEqual boolean> = true; - - f1; - f2; - f3; + util.assertEqual boolean>(true); }); const args2 = z.tuple([ @@ -61,15 +56,14 @@ const func2 = z.function(args2, returns2); test("function inference 2", () => { type func2 = z.TypeOf; - const t2: util.AssertEqual< + util.assertEqual< func2, (arg: { f1: number; f2: string | null; f3?: (boolean | undefined)[] | undefined; }) => string | number - > = true; - [t2]; + >(true); }); test("valid function run", () => { @@ -212,3 +206,32 @@ test("params and returnType getters", () => { func.parameters().items[0].parse("asdf"); func.returnType().parse("asdf"); }); + +test("inference with transforms", () => { + const funcSchema = z + .function() + .args(z.string().transform((val) => val.length)) + .returns(z.object({ val: z.number() })); + const myFunc = funcSchema.implement((val) => { + return { val, extra: "stuff" }; + }); + myFunc("asdf"); + + util.assertEqual< + typeof myFunc, + (arg: string) => { val: number; extra: string } + >(true); +}); + +test("fallback to OuterTypeOfFunction", () => { + const funcSchema = z + .function() + .args(z.string().transform((val) => val.length)) + .returns(z.object({ arg: z.number() }).transform((val) => val.arg)); + + const myFunc = funcSchema.implement((val) => { + return { arg: val, arg2: false }; + }); + + util.assertEqual number>(true); +}); diff --git a/deno/lib/helpers/util.ts b/deno/lib/helpers/util.ts index fe46c050b..dc00e94e6 100644 --- a/deno/lib/helpers/util.ts +++ b/deno/lib/helpers/util.ts @@ -4,6 +4,7 @@ export namespace util { ? true : false : false; + export function assertEqual(_cond: AssertEqual) {} export function assertNever(_x: never): never { throw new Error(); diff --git a/deno/lib/types.ts b/deno/lib/types.ts index e51e2b25e..7becf8740 100644 --- a/deno/lib/types.ts +++ b/deno/lib/types.ts @@ -2915,7 +2915,11 @@ export class ZodFunction< }); } - implement>(func: F): F { + implement>( + func: F + ): ReturnType extends Returns["_output"] + ? (...args: Args["_input"]) => ReturnType + : OuterTypeOfFunction { const validatedFunc = this.parse(func); return validatedFunc as any; } diff --git a/package.json b/package.json index c99a790bd..e7721e571 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zod", - "version": "3.17.6", + "version": "3.17.7", "description": "TypeScript-first schema declaration and validation library with static type inference", "main": "./lib/index.js", "types": "./index.d.ts", diff --git a/src/__tests__/function.test.ts b/src/__tests__/function.test.ts index aaf8fb68f..c50ab6504 100644 --- a/src/__tests__/function.test.ts +++ b/src/__tests__/function.test.ts @@ -25,26 +25,21 @@ test("parsed function fail 2", () => { test("function inference 1", () => { type func1 = z.TypeOf; - const t1: util.AssertEqual number> = true; - [t1]; + util.assertEqual number>(true); }); test("args method", () => { const t1 = z.function(); type t1 = z.infer; - const f1: util.AssertEqual void> = true; + util.assertEqual void>(true); const t2 = t1.args(z.string()); type t2 = z.infer; - const f2: util.AssertEqual void> = true; + util.assertEqual void>(true); const t3 = t2.returns(z.boolean()); type t3 = z.infer; - const f3: util.AssertEqual boolean> = true; - - f1; - f2; - f3; + util.assertEqual boolean>(true); }); const args2 = z.tuple([ @@ -60,15 +55,14 @@ const func2 = z.function(args2, returns2); test("function inference 2", () => { type func2 = z.TypeOf; - const t2: util.AssertEqual< + util.assertEqual< func2, (arg: { f1: number; f2: string | null; f3?: (boolean | undefined)[] | undefined; }) => string | number - > = true; - [t2]; + >(true); }); test("valid function run", () => { @@ -211,3 +205,32 @@ test("params and returnType getters", () => { func.parameters().items[0].parse("asdf"); func.returnType().parse("asdf"); }); + +test("inference with transforms", () => { + const funcSchema = z + .function() + .args(z.string().transform((val) => val.length)) + .returns(z.object({ val: z.number() })); + const myFunc = funcSchema.implement((val) => { + return { val, extra: "stuff" }; + }); + myFunc("asdf"); + + util.assertEqual< + typeof myFunc, + (arg: string) => { val: number; extra: string } + >(true); +}); + +test("fallback to OuterTypeOfFunction", () => { + const funcSchema = z + .function() + .args(z.string().transform((val) => val.length)) + .returns(z.object({ arg: z.number() }).transform((val) => val.arg)); + + const myFunc = funcSchema.implement((val) => { + return { arg: val, arg2: false }; + }); + + util.assertEqual number>(true); +}); diff --git a/src/helpers/util.ts b/src/helpers/util.ts index fe46c050b..dc00e94e6 100644 --- a/src/helpers/util.ts +++ b/src/helpers/util.ts @@ -4,6 +4,7 @@ export namespace util { ? true : false : false; + export function assertEqual(_cond: AssertEqual) {} export function assertNever(_x: never): never { throw new Error(); diff --git a/src/types.ts b/src/types.ts index 4f014884a..d858214e8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2915,7 +2915,11 @@ export class ZodFunction< }); } - implement>(func: F): F { + implement>( + func: F + ): ReturnType extends Returns["_output"] + ? (...args: Args["_input"]) => ReturnType + : OuterTypeOfFunction { const validatedFunc = this.parse(func); return validatedFunc as any; }