Skip to content

Commit 2c3564f

Browse files
committed
feat(convert) add convert methods
1 parent ef120b0 commit 2c3564f

File tree

3 files changed

+179
-1
lines changed

3 files changed

+179
-1
lines changed

deno/lib/__tests__/convert.test-d.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ describe("convert", () => {
1818
expectTypeOf(numberToString.convert).returns.toMatchTypeOf<string>();
1919
});
2020

21-
test("todo", () => {
21+
test("valid schema conversion", () => {
2222
const userSchema = z.object({
2323
id: z.string(),
2424
name: z.string(),
@@ -33,6 +33,14 @@ describe("convert", () => {
3333

3434
const user = userSchema.convert(validInput);
3535
expectTypeOf(user).toMatchTypeOf<z.infer<typeof userSchema>>();
36+
});
37+
38+
test("invalid schema conversion", () => {
39+
const userSchema = z.object({
40+
id: z.string(),
41+
name: z.string(),
42+
age: z.string().transform((age) => parseInt(age, 10)),
43+
});
3644

3745
// Input not matching the schema's input type
3846
const invalidInput = {

src/__tests__/convert.test-d.ts

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import { assertType, describe, expectTypeOf, test } from "vitest";
2+
import { SafeParseReturnType, z } from "../index";
3+
4+
const stringToNumber = z.string().transform((arg) => parseFloat(arg));
5+
const numberToString = z.number().transform((n) => String(n));
6+
7+
const asyncNumberToString = z.number().transform(async (n) => String(n));
8+
const asyncStringToNumber = z.string().transform(async (n) => parseFloat(n));
9+
10+
describe("convert", () => {
11+
test("input and output types to be enforced in", () => {
12+
expectTypeOf(stringToNumber.convert).toBeFunction();
13+
expectTypeOf(stringToNumber.convert).parameter(0).toMatchTypeOf<string>();
14+
expectTypeOf(stringToNumber.convert).returns.toMatchTypeOf<number>();
15+
16+
expectTypeOf(numberToString.convert).toBeFunction();
17+
expectTypeOf(numberToString.convert).parameter(0).toMatchTypeOf<number>();
18+
expectTypeOf(numberToString.convert).returns.toMatchTypeOf<string>();
19+
});
20+
21+
test("valid schema conversion", () => {
22+
const userSchema = z.object({
23+
id: z.string(),
24+
name: z.string(),
25+
age: z.string().transform((age) => parseInt(age, 10)),
26+
});
27+
28+
const validInput: z.input<typeof userSchema> = {
29+
id: "123",
30+
name: "Alice",
31+
age: "25",
32+
};
33+
34+
const user = userSchema.convert(validInput);
35+
expectTypeOf(user).toMatchTypeOf<z.infer<typeof userSchema>>();
36+
});
37+
38+
test("invalid schema conversion", () => {
39+
const userSchema = z.object({
40+
id: z.string(),
41+
name: z.string(),
42+
age: z.string().transform((age) => parseInt(age, 10)),
43+
});
44+
45+
// Input not matching the schema's input type
46+
const invalidInput = {
47+
name: "Alice",
48+
age: "25",
49+
};
50+
51+
expectTypeOf(numberToString.convert)
52+
.parameter(0)
53+
.not.toMatchTypeOf<typeof invalidInput>();
54+
55+
try {
56+
// @ts-expect-error - compile error
57+
userSchema.convert(invalidInput);
58+
} catch {}
59+
});
60+
});
61+
62+
describe("safeConvert", () => {
63+
test("input and output types to be enforced in", () => {
64+
expectTypeOf(stringToNumber.safeConvert).toBeFunction();
65+
expectTypeOf(stringToNumber.safeConvert)
66+
.parameter(0)
67+
.toMatchTypeOf<string>();
68+
expectTypeOf(stringToNumber.safeConvert).returns.toMatchTypeOf<
69+
SafeParseReturnType<string, number>
70+
>();
71+
72+
const resA = stringToNumber.safeConvert(""); // valid input but invlaid output
73+
if (resA.success) {
74+
assertType<number>(resA.data);
75+
} else {
76+
assertType<z.ZodError>(resA.error);
77+
}
78+
79+
expectTypeOf(numberToString.safeConvert).toBeFunction();
80+
expectTypeOf(numberToString.safeConvert)
81+
.parameter(0)
82+
.toMatchTypeOf<number>();
83+
expectTypeOf(numberToString.safeConvert).returns.toMatchTypeOf<
84+
SafeParseReturnType<number, string>
85+
>();
86+
87+
const resB = numberToString.safeConvert(321);
88+
if (resB.success) {
89+
assertType<string>(resB.data);
90+
} else {
91+
assertType<z.ZodError>(resB.error);
92+
}
93+
});
94+
});
95+
96+
describe("convertAsync", () => {
97+
test("input and output types to be enforced in", () => {
98+
expectTypeOf(asyncStringToNumber.convertAsync).toBeFunction();
99+
expectTypeOf(asyncStringToNumber.convertAsync)
100+
.parameter(0)
101+
.toMatchTypeOf<string>();
102+
expectTypeOf(asyncStringToNumber.convertAsync).returns.toMatchTypeOf<
103+
Promise<number>
104+
>();
105+
106+
expectTypeOf(asyncNumberToString.convertAsync).toBeFunction();
107+
expectTypeOf(asyncNumberToString.convertAsync)
108+
.parameter(0)
109+
.toMatchTypeOf<number>();
110+
expectTypeOf(asyncNumberToString.convertAsync).returns.toMatchTypeOf<
111+
Promise<string>
112+
>();
113+
});
114+
});
115+
116+
describe("safeConvertAsync", () => {
117+
test("input and output types to be enforced in", async () => {
118+
expectTypeOf(asyncStringToNumber.safeConvertAsync).toBeFunction();
119+
expectTypeOf(asyncStringToNumber.safeConvertAsync)
120+
.parameter(0)
121+
.toMatchTypeOf<string>();
122+
expectTypeOf(asyncStringToNumber.safeConvertAsync).returns.toMatchTypeOf<
123+
Promise<SafeParseReturnType<string, number>>
124+
>();
125+
126+
const resA = await asyncStringToNumber.safeConvertAsync(""); // valid input but invlaid output
127+
if (resA.success) {
128+
assertType<number>(resA.data);
129+
} else {
130+
assertType<z.ZodError>(resA.error);
131+
}
132+
133+
expectTypeOf(asyncNumberToString.safeConvertAsync).toBeFunction();
134+
expectTypeOf(asyncNumberToString.safeConvertAsync)
135+
.parameter(0)
136+
.toMatchTypeOf<number>();
137+
expectTypeOf(asyncNumberToString.safeConvertAsync).returns.toMatchTypeOf<
138+
Promise<SafeParseReturnType<number, string>>
139+
>();
140+
141+
const resB = await asyncNumberToString.safeConvertAsync(321);
142+
if (resB.success) {
143+
assertType<string>(resB.data);
144+
} else {
145+
assertType<z.ZodError>(resB.error);
146+
}
147+
});
148+
});

src/types.ts

+22
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,28 @@ export abstract class ZodType<
517517
isNullable(): boolean {
518518
return this.safeParse(null).success;
519519
}
520+
521+
convert(data: Input, params?: Partial<ParseParams>): Output {
522+
return this.parse(data, params);
523+
}
524+
525+
safeConvert(
526+
data: Input,
527+
params?: Partial<ParseParams>
528+
): SafeParseReturnType<Input, Output> {
529+
return this.safeParse(data, params);
530+
}
531+
532+
convertAsync(data: Input, params?: Partial<ParseParams>): Promise<Output> {
533+
return this.parseAsync(data, params);
534+
}
535+
536+
safeConvertAsync(
537+
data: Input,
538+
params?: Partial<ParseParams>
539+
): Promise<SafeParseReturnType<Input, Output>> {
540+
return this.safeParseAsync(data, params);
541+
}
520542
}
521543

522544
/////////////////////////////////////////

0 commit comments

Comments
 (0)