Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial prototype fix for issue #2651 #2652

Merged
merged 3 commits into from Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 26 additions & 0 deletions deno/lib/__tests__/function.test.ts
Expand Up @@ -29,6 +29,32 @@ test("function inference 1", () => {
util.assertEqual<func1, (k: string) => number>(true);
});

test("method parsing", () => {
const methodObject = z.object({
property: z.number(),
method: z.function().args(z.string()).returns(z.number())
});
const methodInstance = {
property: 3,
method: function(s: string) { return s.length + this.property; }
};
const parsed = methodObject.parse(methodInstance);
expect(parsed.method('length=8')).toBe(11); // 8 length + 3 property
});

test("async method parsing", async () => {
const methodObject = z.object({
property: z.number(),
method: z.function().args(z.string()).returns(z.promise(z.number()))
});
const methodInstance = {
property: 3,
method: async function(s: string) { return s.length + this.property; }
};
const parsed = methodObject.parse(methodInstance);
expect(await parsed.method('length=8')).toBe(11); // 8 length + 3 property
});

test("args method", () => {
const t1 = z.function();
type t1 = z.infer<typeof t1>;
Expand Down
24 changes: 16 additions & 8 deletions deno/lib/types.ts
Expand Up @@ -3732,17 +3732,21 @@ export class ZodFunction<
const fn = ctx.data;

if (this._def.returns instanceof ZodPromise) {
return OK(async (...args: any[]) => {
// Would love a way to avoid disabling this rule, but we need
// an alias (using an arrow function was what caused 2651).
// eslint-disable-next-line @typescript-eslint/no-this-alias
const me = this;
return OK(async function (this: any, ...args: any[]) {
const error = new ZodError([]);
const parsedArgs = await this._def.args
const parsedArgs = await me._def.args
.parseAsync(args, params)
.catch((e) => {
error.addIssue(makeArgsIssue(args, e));
throw error;
});
const result = await fn(...(parsedArgs as any));
const result = await Reflect.apply(fn, this, parsedArgs as any);
const parsedReturns = await (
this._def.returns as unknown as ZodPromise<ZodTypeAny>
me._def.returns as unknown as ZodPromise<ZodTypeAny>
)._def.type
.parseAsync(result, params)
.catch((e) => {
Expand All @@ -3752,13 +3756,17 @@ export class ZodFunction<
return parsedReturns;
});
} else {
return OK((...args: any[]) => {
const parsedArgs = this._def.args.safeParse(args, params);
// Would love a way to avoid disabling this rule, but we need
// an alias (using an arrow function was what caused 2651).
// eslint-disable-next-line @typescript-eslint/no-this-alias
const me = this;
return OK(function (this: any, ...args: any[]) {
const parsedArgs = me._def.args.safeParse(args, params);
if (!parsedArgs.success) {
throw new ZodError([makeArgsIssue(args, parsedArgs.error)]);
}
const result = fn(...(parsedArgs.data as any));
const parsedReturns = this._def.returns.safeParse(result, params);
const result = Reflect.apply(fn, this, parsedArgs.data);
const parsedReturns = me._def.returns.safeParse(result, params);
if (!parsedReturns.success) {
throw new ZodError([makeReturnsIssue(result, parsedReturns.error)]);
}
Expand Down
26 changes: 26 additions & 0 deletions src/__tests__/function.test.ts
Expand Up @@ -28,6 +28,32 @@ test("function inference 1", () => {
util.assertEqual<func1, (k: string) => number>(true);
});

test("method parsing", () => {
const methodObject = z.object({
property: z.number(),
method: z.function().args(z.string()).returns(z.number())
});
const methodInstance = {
property: 3,
method: function(s: string) { return s.length + this.property; }
};
const parsed = methodObject.parse(methodInstance);
expect(parsed.method('length=8')).toBe(11); // 8 length + 3 property
});

test("async method parsing", async () => {
const methodObject = z.object({
property: z.number(),
method: z.function().args(z.string()).returns(z.promise(z.number()))
});
const methodInstance = {
property: 3,
method: async function(s: string) { return s.length + this.property; }
};
const parsed = methodObject.parse(methodInstance);
expect(await parsed.method('length=8')).toBe(11); // 8 length + 3 property
});

test("args method", () => {
const t1 = z.function();
type t1 = z.infer<typeof t1>;
Expand Down
24 changes: 16 additions & 8 deletions src/types.ts
Expand Up @@ -3732,17 +3732,21 @@ export class ZodFunction<
const fn = ctx.data;

if (this._def.returns instanceof ZodPromise) {
return OK(async (...args: any[]) => {
// Would love a way to avoid disabling this rule, but we need
// an alias (using an arrow function was what caused 2651).
// eslint-disable-next-line @typescript-eslint/no-this-alias
const me = this;
return OK(async function (this: any, ...args: any[]) {
const error = new ZodError([]);
const parsedArgs = await this._def.args
const parsedArgs = await me._def.args
.parseAsync(args, params)
.catch((e) => {
error.addIssue(makeArgsIssue(args, e));
throw error;
});
const result = await fn(...(parsedArgs as any));
const result = await Reflect.apply(fn, this, parsedArgs as any);
const parsedReturns = await (
this._def.returns as unknown as ZodPromise<ZodTypeAny>
me._def.returns as unknown as ZodPromise<ZodTypeAny>
)._def.type
.parseAsync(result, params)
.catch((e) => {
Expand All @@ -3752,13 +3756,17 @@ export class ZodFunction<
return parsedReturns;
});
} else {
return OK((...args: any[]) => {
const parsedArgs = this._def.args.safeParse(args, params);
// Would love a way to avoid disabling this rule, but we need
// an alias (using an arrow function was what caused 2651).
// eslint-disable-next-line @typescript-eslint/no-this-alias
const me = this;
return OK(function (this: any, ...args: any[]) {
const parsedArgs = me._def.args.safeParse(args, params);
if (!parsedArgs.success) {
throw new ZodError([makeArgsIssue(args, parsedArgs.error)]);
}
const result = fn(...(parsedArgs.data as any));
const parsedReturns = this._def.returns.safeParse(result, params);
const result = Reflect.apply(fn, this, parsedArgs.data);
const parsedReturns = me._def.returns.safeParse(result, params);
if (!parsedReturns.success) {
throw new ZodError([makeReturnsIssue(result, parsedReturns.error)]);
}
Expand Down