Skip to content

Commit 064b229

Browse files
committed
feat(convert) add convert methods
1 parent 18a97a4 commit 064b229

File tree

2 files changed

+162
-0
lines changed

2 files changed

+162
-0
lines changed

src/__tests__/convert.test-d.ts

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
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("todo", () => {
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+
// Input not matching the schema's input type
38+
const invalidInput = {
39+
name: "Alice",
40+
age: "25",
41+
};
42+
43+
expectTypeOf(numberToString.convert)
44+
.parameter(0)
45+
.not.toMatchTypeOf<typeof invalidInput>();
46+
47+
try {
48+
// @ts-expect-error - compile error
49+
userSchema.convert(invalidInput);
50+
} catch {}
51+
});
52+
});
53+
54+
describe("safeConvert", () => {
55+
test("input and output types to be enforced in", () => {
56+
expectTypeOf(stringToNumber.safeConvert).toBeFunction();
57+
expectTypeOf(stringToNumber.safeConvert)
58+
.parameter(0)
59+
.toMatchTypeOf<string>();
60+
expectTypeOf(stringToNumber.safeConvert).returns.toMatchTypeOf<
61+
SafeParseReturnType<string, number>
62+
>();
63+
64+
const resA = stringToNumber.safeConvert(""); // valid input but invlaid output
65+
if (resA.success) {
66+
assertType<number>(resA.data);
67+
} else {
68+
assertType<z.ZodError>(resA.error);
69+
}
70+
71+
expectTypeOf(numberToString.safeConvert).toBeFunction();
72+
expectTypeOf(numberToString.safeConvert)
73+
.parameter(0)
74+
.toMatchTypeOf<number>();
75+
expectTypeOf(numberToString.safeConvert).returns.toMatchTypeOf<
76+
SafeParseReturnType<number, string>
77+
>();
78+
79+
const resB = numberToString.safeConvert(321);
80+
if (resB.success) {
81+
assertType<string>(resB.data);
82+
} else {
83+
assertType<z.ZodError>(resB.error);
84+
}
85+
});
86+
});
87+
88+
describe("convertAsync", () => {
89+
test("input and output types to be enforced in", () => {
90+
expectTypeOf(asyncStringToNumber.convertAsync).toBeFunction();
91+
expectTypeOf(asyncStringToNumber.convertAsync)
92+
.parameter(0)
93+
.toMatchTypeOf<string>();
94+
expectTypeOf(asyncStringToNumber.convertAsync).returns.toMatchTypeOf<
95+
Promise<number>
96+
>();
97+
98+
expectTypeOf(asyncNumberToString.convertAsync).toBeFunction();
99+
expectTypeOf(asyncNumberToString.convertAsync)
100+
.parameter(0)
101+
.toMatchTypeOf<number>();
102+
expectTypeOf(asyncNumberToString.convertAsync).returns.toMatchTypeOf<
103+
Promise<string>
104+
>();
105+
});
106+
});
107+
108+
describe("safeConvertAsync", () => {
109+
test("input and output types to be enforced in", async () => {
110+
expectTypeOf(asyncStringToNumber.safeConvertAsync).toBeFunction();
111+
expectTypeOf(asyncStringToNumber.safeConvertAsync)
112+
.parameter(0)
113+
.toMatchTypeOf<string>();
114+
expectTypeOf(asyncStringToNumber.safeConvertAsync).returns.toMatchTypeOf<
115+
Promise<SafeParseReturnType<string, number>>
116+
>();
117+
118+
const resA = await asyncStringToNumber.safeConvertAsync(""); // valid input but invlaid output
119+
if (resA.success) {
120+
assertType<number>(resA.data);
121+
} else {
122+
assertType<z.ZodError>(resA.error);
123+
}
124+
125+
expectTypeOf(asyncNumberToString.safeConvertAsync).toBeFunction();
126+
expectTypeOf(asyncNumberToString.safeConvertAsync)
127+
.parameter(0)
128+
.toMatchTypeOf<number>();
129+
expectTypeOf(asyncNumberToString.safeConvertAsync).returns.toMatchTypeOf<
130+
Promise<SafeParseReturnType<number, string>>
131+
>();
132+
133+
const resB = await asyncNumberToString.safeConvertAsync(321);
134+
if (resB.success) {
135+
assertType<string>(resB.data);
136+
} else {
137+
assertType<z.ZodError>(resB.error);
138+
}
139+
});
140+
});

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)