diff --git a/src/__tests__/convert.test-d.ts b/src/__tests__/convert.test-d.ts new file mode 100644 index 000000000..e4120ff44 --- /dev/null +++ b/src/__tests__/convert.test-d.ts @@ -0,0 +1,140 @@ +import { assertType, describe, expectTypeOf, test } from "vitest"; +import { SafeParseReturnType, z } from "../index"; + +const stringToNumber = z.string().transform((arg) => parseFloat(arg)); +const numberToString = z.number().transform((n) => String(n)); + +const asyncNumberToString = z.number().transform(async (n) => String(n)); +const asyncStringToNumber = z.string().transform(async (n) => parseFloat(n)); + +describe("convert", () => { + test("input and output types to be enforced in", () => { + expectTypeOf(stringToNumber.convert).toBeFunction(); + expectTypeOf(stringToNumber.convert).parameter(0).toMatchTypeOf(); + expectTypeOf(stringToNumber.convert).returns.toMatchTypeOf(); + + expectTypeOf(numberToString.convert).toBeFunction(); + expectTypeOf(numberToString.convert).parameter(0).toMatchTypeOf(); + expectTypeOf(numberToString.convert).returns.toMatchTypeOf(); + }); + + test("todo", () => { + const userSchema = z.object({ + id: z.string(), + name: z.string(), + age: z.string().transform((age) => parseInt(age, 10)), + }); + + const validInput: z.input = { + id: "123", + name: "Alice", + age: "25", + }; + + const user = userSchema.convert(validInput); + expectTypeOf(user).toMatchTypeOf>(); + + // Input not matching the schema's input type + const invalidInput = { + name: "Alice", + age: "25", + }; + + expectTypeOf(numberToString.convert) + .parameter(0) + .not.toMatchTypeOf(); + + try { + // @ts-expect-error - compile error + userSchema.convert(invalidInput); + } catch {} + }); +}); + +describe("safeConvert", () => { + test("input and output types to be enforced in", () => { + expectTypeOf(stringToNumber.safeConvert).toBeFunction(); + expectTypeOf(stringToNumber.safeConvert) + .parameter(0) + .toMatchTypeOf(); + expectTypeOf(stringToNumber.safeConvert).returns.toMatchTypeOf< + SafeParseReturnType + >(); + + const resA = stringToNumber.safeConvert(""); // valid input but invlaid output + if (resA.success) { + assertType(resA.data); + } else { + assertType(resA.error); + } + + expectTypeOf(numberToString.safeConvert).toBeFunction(); + expectTypeOf(numberToString.safeConvert) + .parameter(0) + .toMatchTypeOf(); + expectTypeOf(numberToString.safeConvert).returns.toMatchTypeOf< + SafeParseReturnType + >(); + + const resB = numberToString.safeConvert(321); + if (resB.success) { + assertType(resB.data); + } else { + assertType(resB.error); + } + }); +}); + +describe("convertAsync", () => { + test("input and output types to be enforced in", () => { + expectTypeOf(asyncStringToNumber.convertAsync).toBeFunction(); + expectTypeOf(asyncStringToNumber.convertAsync) + .parameter(0) + .toMatchTypeOf(); + expectTypeOf(asyncStringToNumber.convertAsync).returns.toMatchTypeOf< + Promise + >(); + + expectTypeOf(asyncNumberToString.convertAsync).toBeFunction(); + expectTypeOf(asyncNumberToString.convertAsync) + .parameter(0) + .toMatchTypeOf(); + expectTypeOf(asyncNumberToString.convertAsync).returns.toMatchTypeOf< + Promise + >(); + }); +}); + +describe("safeConvertAsync", () => { + test("input and output types to be enforced in", async () => { + expectTypeOf(asyncStringToNumber.safeConvertAsync).toBeFunction(); + expectTypeOf(asyncStringToNumber.safeConvertAsync) + .parameter(0) + .toMatchTypeOf(); + expectTypeOf(asyncStringToNumber.safeConvertAsync).returns.toMatchTypeOf< + Promise> + >(); + + const resA = await asyncStringToNumber.safeConvertAsync(""); // valid input but invlaid output + if (resA.success) { + assertType(resA.data); + } else { + assertType(resA.error); + } + + expectTypeOf(asyncNumberToString.safeConvertAsync).toBeFunction(); + expectTypeOf(asyncNumberToString.safeConvertAsync) + .parameter(0) + .toMatchTypeOf(); + expectTypeOf(asyncNumberToString.safeConvertAsync).returns.toMatchTypeOf< + Promise> + >(); + + const resB = await asyncNumberToString.safeConvertAsync(321); + if (resB.success) { + assertType(resB.data); + } else { + assertType(resB.error); + } + }); +}); diff --git a/src/types.ts b/src/types.ts index f3730ae14..3ccfe6b53 100644 --- a/src/types.ts +++ b/src/types.ts @@ -517,6 +517,28 @@ export abstract class ZodType< isNullable(): boolean { return this.safeParse(null).success; } + + convert(data: Input, params?: Partial): Output { + return this.parse(data, params); + } + + safeConvert( + data: Input, + params?: Partial + ): SafeParseReturnType { + return this.safeParse(data, params); + } + + convertAsync(data: Input, params?: Partial): Promise { + return this.parseAsync(data, params); + } + + safeConvertAsync( + data: Input, + params?: Partial + ): Promise> { + return this.safeParseAsync(data, params); + } } /////////////////////////////////////////