Skip to content

Commit 932cc47

Browse files
authoredAug 15, 2023
Initial prototype fix for issue #2651 (#2652)
* Initial prototype fix * Add implementation/test for async * Fix formatting
1 parent fba438c commit 932cc47

File tree

4 files changed

+84
-16
lines changed

4 files changed

+84
-16
lines changed
 

‎deno/lib/__tests__/function.test.ts

+26
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,32 @@ test("function inference 1", () => {
2929
util.assertEqual<func1, (k: string) => number>(true);
3030
});
3131

32+
test("method parsing", () => {
33+
const methodObject = z.object({
34+
property: z.number(),
35+
method: z.function().args(z.string()).returns(z.number())
36+
});
37+
const methodInstance = {
38+
property: 3,
39+
method: function(s: string) { return s.length + this.property; }
40+
};
41+
const parsed = methodObject.parse(methodInstance);
42+
expect(parsed.method('length=8')).toBe(11); // 8 length + 3 property
43+
});
44+
45+
test("async method parsing", async () => {
46+
const methodObject = z.object({
47+
property: z.number(),
48+
method: z.function().args(z.string()).returns(z.promise(z.number()))
49+
});
50+
const methodInstance = {
51+
property: 3,
52+
method: async function(s: string) { return s.length + this.property; }
53+
};
54+
const parsed = methodObject.parse(methodInstance);
55+
expect(await parsed.method('length=8')).toBe(11); // 8 length + 3 property
56+
});
57+
3258
test("args method", () => {
3359
const t1 = z.function();
3460
type t1 = z.infer<typeof t1>;

‎deno/lib/types.ts

+16-8
Original file line numberDiff line numberDiff line change
@@ -3736,17 +3736,21 @@ export class ZodFunction<
37363736
const fn = ctx.data;
37373737

37383738
if (this._def.returns instanceof ZodPromise) {
3739-
return OK(async (...args: any[]) => {
3739+
// Would love a way to avoid disabling this rule, but we need
3740+
// an alias (using an arrow function was what caused 2651).
3741+
// eslint-disable-next-line @typescript-eslint/no-this-alias
3742+
const me = this;
3743+
return OK(async function (this: any, ...args: any[]) {
37403744
const error = new ZodError([]);
3741-
const parsedArgs = await this._def.args
3745+
const parsedArgs = await me._def.args
37423746
.parseAsync(args, params)
37433747
.catch((e) => {
37443748
error.addIssue(makeArgsIssue(args, e));
37453749
throw error;
37463750
});
3747-
const result = await fn(...(parsedArgs as any));
3751+
const result = await Reflect.apply(fn, this, parsedArgs as any);
37483752
const parsedReturns = await (
3749-
this._def.returns as unknown as ZodPromise<ZodTypeAny>
3753+
me._def.returns as unknown as ZodPromise<ZodTypeAny>
37503754
)._def.type
37513755
.parseAsync(result, params)
37523756
.catch((e) => {
@@ -3756,13 +3760,17 @@ export class ZodFunction<
37563760
return parsedReturns;
37573761
});
37583762
} else {
3759-
return OK((...args: any[]) => {
3760-
const parsedArgs = this._def.args.safeParse(args, params);
3763+
// Would love a way to avoid disabling this rule, but we need
3764+
// an alias (using an arrow function was what caused 2651).
3765+
// eslint-disable-next-line @typescript-eslint/no-this-alias
3766+
const me = this;
3767+
return OK(function (this: any, ...args: any[]) {
3768+
const parsedArgs = me._def.args.safeParse(args, params);
37613769
if (!parsedArgs.success) {
37623770
throw new ZodError([makeArgsIssue(args, parsedArgs.error)]);
37633771
}
3764-
const result = fn(...(parsedArgs.data as any));
3765-
const parsedReturns = this._def.returns.safeParse(result, params);
3772+
const result = Reflect.apply(fn, this, parsedArgs.data);
3773+
const parsedReturns = me._def.returns.safeParse(result, params);
37663774
if (!parsedReturns.success) {
37673775
throw new ZodError([makeReturnsIssue(result, parsedReturns.error)]);
37683776
}

‎src/__tests__/function.test.ts

+26
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,32 @@ test("function inference 1", () => {
2828
util.assertEqual<func1, (k: string) => number>(true);
2929
});
3030

31+
test("method parsing", () => {
32+
const methodObject = z.object({
33+
property: z.number(),
34+
method: z.function().args(z.string()).returns(z.number())
35+
});
36+
const methodInstance = {
37+
property: 3,
38+
method: function(s: string) { return s.length + this.property; }
39+
};
40+
const parsed = methodObject.parse(methodInstance);
41+
expect(parsed.method('length=8')).toBe(11); // 8 length + 3 property
42+
});
43+
44+
test("async method parsing", async () => {
45+
const methodObject = z.object({
46+
property: z.number(),
47+
method: z.function().args(z.string()).returns(z.promise(z.number()))
48+
});
49+
const methodInstance = {
50+
property: 3,
51+
method: async function(s: string) { return s.length + this.property; }
52+
};
53+
const parsed = methodObject.parse(methodInstance);
54+
expect(await parsed.method('length=8')).toBe(11); // 8 length + 3 property
55+
});
56+
3157
test("args method", () => {
3258
const t1 = z.function();
3359
type t1 = z.infer<typeof t1>;

‎src/types.ts

+16-8
Original file line numberDiff line numberDiff line change
@@ -3736,17 +3736,21 @@ export class ZodFunction<
37363736
const fn = ctx.data;
37373737

37383738
if (this._def.returns instanceof ZodPromise) {
3739-
return OK(async (...args: any[]) => {
3739+
// Would love a way to avoid disabling this rule, but we need
3740+
// an alias (using an arrow function was what caused 2651).
3741+
// eslint-disable-next-line @typescript-eslint/no-this-alias
3742+
const me = this;
3743+
return OK(async function (this: any, ...args: any[]) {
37403744
const error = new ZodError([]);
3741-
const parsedArgs = await this._def.args
3745+
const parsedArgs = await me._def.args
37423746
.parseAsync(args, params)
37433747
.catch((e) => {
37443748
error.addIssue(makeArgsIssue(args, e));
37453749
throw error;
37463750
});
3747-
const result = await fn(...(parsedArgs as any));
3751+
const result = await Reflect.apply(fn, this, parsedArgs as any);
37483752
const parsedReturns = await (
3749-
this._def.returns as unknown as ZodPromise<ZodTypeAny>
3753+
me._def.returns as unknown as ZodPromise<ZodTypeAny>
37503754
)._def.type
37513755
.parseAsync(result, params)
37523756
.catch((e) => {
@@ -3756,13 +3760,17 @@ export class ZodFunction<
37563760
return parsedReturns;
37573761
});
37583762
} else {
3759-
return OK((...args: any[]) => {
3760-
const parsedArgs = this._def.args.safeParse(args, params);
3763+
// Would love a way to avoid disabling this rule, but we need
3764+
// an alias (using an arrow function was what caused 2651).
3765+
// eslint-disable-next-line @typescript-eslint/no-this-alias
3766+
const me = this;
3767+
return OK(function (this: any, ...args: any[]) {
3768+
const parsedArgs = me._def.args.safeParse(args, params);
37613769
if (!parsedArgs.success) {
37623770
throw new ZodError([makeArgsIssue(args, parsedArgs.error)]);
37633771
}
3764-
const result = fn(...(parsedArgs.data as any));
3765-
const parsedReturns = this._def.returns.safeParse(result, params);
3772+
const result = Reflect.apply(fn, this, parsedArgs.data);
3773+
const parsedReturns = me._def.returns.safeParse(result, params);
37663774
if (!parsedReturns.success) {
37673775
throw new ZodError([makeReturnsIssue(result, parsedReturns.error)]);
37683776
}

0 commit comments

Comments
 (0)
Please sign in to comment.