Skip to content

Commit 1dd44a0

Browse files
committedFeb 11, 2025·
Support async z.custom
1 parent b7e173d commit 1dd44a0

File tree

4 files changed

+80
-22
lines changed

4 files changed

+80
-22
lines changed
 

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

+13
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,16 @@ test("string params", () => {
1717
// @ts-ignore
1818
expect(JSON.stringify(result.error).includes("customerr")).toEqual(true);
1919
});
20+
21+
test("async validations", async () => {
22+
const example1 = z.custom<number>(async (x) => {
23+
return typeof x === "number";
24+
});
25+
const r1 = await example1.safeParseAsync(1234);
26+
expect(r1.success).toEqual(true);
27+
expect(r1.data).toEqual(1234);
28+
29+
const r2 = await example1.safeParseAsync("asdf");
30+
expect(r2.success).toEqual(false);
31+
expect(r2.error!.issues.length).toEqual(1);
32+
});

‎deno/lib/types.ts

+27-11
Original file line numberDiff line numberDiff line change
@@ -5235,10 +5235,21 @@ export class ZodReadonly<T extends ZodTypeAny> extends ZodType<
52355235
////////// //////////
52365236
////////////////////////////////////////
52375237
////////////////////////////////////////
5238+
function cleanParams(params: unknown, data: unknown) {
5239+
const p =
5240+
typeof params === "function"
5241+
? params(data)
5242+
: typeof params === "string"
5243+
? { message: params }
5244+
: params;
5245+
5246+
const p2 = typeof p === "string" ? { message: p } : p;
5247+
return p2;
5248+
}
52385249
type CustomParams = CustomErrorParams & { fatal?: boolean };
52395250
export function custom<T>(
52405251
check?: (data: any) => any,
5241-
params: string | CustomParams | ((input: any) => CustomParams) = {},
5252+
_params: string | CustomParams | ((input: any) => CustomParams) = {},
52425253
/**
52435254
* @deprecated
52445255
*
@@ -5253,17 +5264,22 @@ export function custom<T>(
52535264
): ZodType<T, ZodTypeDef, T> {
52545265
if (check)
52555266
return ZodAny.create().superRefine((data, ctx) => {
5256-
if (!check(data)) {
5257-
const p =
5258-
typeof params === "function"
5259-
? params(data)
5260-
: typeof params === "string"
5261-
? { message: params }
5262-
: params;
5263-
const _fatal = p.fatal ?? fatal ?? true;
5264-
const p2 = typeof p === "string" ? { message: p } : p;
5265-
ctx.addIssue({ code: "custom", ...p2, fatal: _fatal });
5267+
const r = check(data);
5268+
if (r instanceof Promise) {
5269+
return r.then((r) => {
5270+
if (!r) {
5271+
const params = cleanParams(_params, data);
5272+
const _fatal = params.fatal ?? fatal ?? true;
5273+
ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
5274+
}
5275+
});
5276+
}
5277+
if (!r) {
5278+
const params = cleanParams(_params, data);
5279+
const _fatal = params.fatal ?? fatal ?? true;
5280+
ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
52665281
}
5282+
return;
52675283
});
52685284
return ZodAny.create();
52695285
}

‎src/__tests__/custom.test.ts

+13
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,16 @@ test("string params", () => {
1616
// @ts-ignore
1717
expect(JSON.stringify(result.error).includes("customerr")).toEqual(true);
1818
});
19+
20+
test("async validations", async () => {
21+
const example1 = z.custom<number>(async (x) => {
22+
return typeof x === "number";
23+
});
24+
const r1 = await example1.safeParseAsync(1234);
25+
expect(r1.success).toEqual(true);
26+
expect(r1.data).toEqual(1234);
27+
28+
const r2 = await example1.safeParseAsync("asdf");
29+
expect(r2.success).toEqual(false);
30+
expect(r2.error!.issues.length).toEqual(1);
31+
});

‎src/types.ts

+27-11
Original file line numberDiff line numberDiff line change
@@ -5235,10 +5235,21 @@ export class ZodReadonly<T extends ZodTypeAny> extends ZodType<
52355235
////////// //////////
52365236
////////////////////////////////////////
52375237
////////////////////////////////////////
5238+
function cleanParams(params: unknown, data: unknown) {
5239+
const p =
5240+
typeof params === "function"
5241+
? params(data)
5242+
: typeof params === "string"
5243+
? { message: params }
5244+
: params;
5245+
5246+
const p2 = typeof p === "string" ? { message: p } : p;
5247+
return p2;
5248+
}
52385249
type CustomParams = CustomErrorParams & { fatal?: boolean };
52395250
export function custom<T>(
52405251
check?: (data: any) => any,
5241-
params: string | CustomParams | ((input: any) => CustomParams) = {},
5252+
_params: string | CustomParams | ((input: any) => CustomParams) = {},
52425253
/**
52435254
* @deprecated
52445255
*
@@ -5253,17 +5264,22 @@ export function custom<T>(
52535264
): ZodType<T, ZodTypeDef, T> {
52545265
if (check)
52555266
return ZodAny.create().superRefine((data, ctx) => {
5256-
if (!check(data)) {
5257-
const p =
5258-
typeof params === "function"
5259-
? params(data)
5260-
: typeof params === "string"
5261-
? { message: params }
5262-
: params;
5263-
const _fatal = p.fatal ?? fatal ?? true;
5264-
const p2 = typeof p === "string" ? { message: p } : p;
5265-
ctx.addIssue({ code: "custom", ...p2, fatal: _fatal });
5267+
const r = check(data);
5268+
if (r instanceof Promise) {
5269+
return r.then((r) => {
5270+
if (!r) {
5271+
const params = cleanParams(_params, data);
5272+
const _fatal = params.fatal ?? fatal ?? true;
5273+
ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
5274+
}
5275+
});
5276+
}
5277+
if (!r) {
5278+
const params = cleanParams(_params, data);
5279+
const _fatal = params.fatal ?? fatal ?? true;
5280+
ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
52665281
}
5282+
return;
52675283
});
52685284
return ZodAny.create();
52695285
}

0 commit comments

Comments
 (0)
Please sign in to comment.