Skip to content

Commit

Permalink
feat(convert) add convert methods
Browse files Browse the repository at this point in the history
  • Loading branch information
oberbeck committed Nov 19, 2024
1 parent 1728ac5 commit dad8820
Show file tree
Hide file tree
Showing 4 changed files with 340 additions and 0 deletions.
148 changes: 148 additions & 0 deletions deno/lib/__tests__/convert.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import { assertType, describe, expectTypeOf, test } from "vitest";
import { SafeParseReturnType, z } from "../index.ts";

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<string>();
expectTypeOf(stringToNumber.convert).returns.toMatchTypeOf<number>();

expectTypeOf(numberToString.convert).toBeFunction();
expectTypeOf(numberToString.convert).parameter(0).toMatchTypeOf<number>();
expectTypeOf(numberToString.convert).returns.toMatchTypeOf<string>();
});

test("valid schema conversion", () => {
const userSchema = z.object({
id: z.string(),
name: z.string(),
age: z.string().transform((age) => parseInt(age, 10)),
});

const validInput: z.input<typeof userSchema> = {
id: "123",
name: "Alice",
age: "25",
};

const user = userSchema.convert(validInput);
expectTypeOf(user).toMatchTypeOf<z.infer<typeof userSchema>>();
});

test("invalid schema conversion", () => {
const userSchema = z.object({
id: z.string(),
name: z.string(),
age: z.string().transform((age) => parseInt(age, 10)),
});

// Input not matching the schema's input type
const invalidInput = {
name: "Alice",
age: "25",
};

expectTypeOf(numberToString.convert)
.parameter(0)
.not.toMatchTypeOf<typeof invalidInput>();

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<string>();
expectTypeOf(stringToNumber.safeConvert).returns.toMatchTypeOf<
SafeParseReturnType<string, number>
>();

const resA = stringToNumber.safeConvert(""); // valid input but invlaid output
if (resA.success) {
assertType<number>(resA.data);
} else {
assertType<z.ZodError>(resA.error);
}

expectTypeOf(numberToString.safeConvert).toBeFunction();
expectTypeOf(numberToString.safeConvert)
.parameter(0)
.toMatchTypeOf<number>();
expectTypeOf(numberToString.safeConvert).returns.toMatchTypeOf<
SafeParseReturnType<number, string>
>();

const resB = numberToString.safeConvert(321);
if (resB.success) {
assertType<string>(resB.data);
} else {
assertType<z.ZodError>(resB.error);
}
});
});

describe("convertAsync", () => {
test("input and output types to be enforced in", () => {
expectTypeOf(asyncStringToNumber.convertAsync).toBeFunction();
expectTypeOf(asyncStringToNumber.convertAsync)
.parameter(0)
.toMatchTypeOf<string>();
expectTypeOf(asyncStringToNumber.convertAsync).returns.toMatchTypeOf<
Promise<number>
>();

expectTypeOf(asyncNumberToString.convertAsync).toBeFunction();
expectTypeOf(asyncNumberToString.convertAsync)
.parameter(0)
.toMatchTypeOf<number>();
expectTypeOf(asyncNumberToString.convertAsync).returns.toMatchTypeOf<
Promise<string>
>();
});
});

describe("safeConvertAsync", () => {
test("input and output types to be enforced in", async () => {
expectTypeOf(asyncStringToNumber.safeConvertAsync).toBeFunction();
expectTypeOf(asyncStringToNumber.safeConvertAsync)
.parameter(0)
.toMatchTypeOf<string>();
expectTypeOf(asyncStringToNumber.safeConvertAsync).returns.toMatchTypeOf<
Promise<SafeParseReturnType<string, number>>
>();

const resA = await asyncStringToNumber.safeConvertAsync(""); // valid input but invlaid output
if (resA.success) {
assertType<number>(resA.data);
} else {
assertType<z.ZodError>(resA.error);
}

expectTypeOf(asyncNumberToString.safeConvertAsync).toBeFunction();
expectTypeOf(asyncNumberToString.safeConvertAsync)
.parameter(0)
.toMatchTypeOf<number>();
expectTypeOf(asyncNumberToString.safeConvertAsync).returns.toMatchTypeOf<
Promise<SafeParseReturnType<number, string>>
>();

const resB = await asyncNumberToString.safeConvertAsync(321);
if (resB.success) {
assertType<string>(resB.data);
} else {
assertType<z.ZodError>(resB.error);
}
});
});
22 changes: 22 additions & 0 deletions deno/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,28 @@ export abstract class ZodType<
isNullable(): boolean {
return this.safeParse(null).success;
}

convert(data: Input, params?: Partial<ParseParams>): Output {
return this.parse(data, params);
}

safeConvert(
data: Input,
params?: Partial<ParseParams>
): SafeParseReturnType<Input, Output> {
return this.safeParse(data, params);
}

convertAsync(data: Input, params?: Partial<ParseParams>): Promise<Output> {
return this.parseAsync(data, params);
}

safeConvertAsync(
data: Input,
params?: Partial<ParseParams>
): Promise<SafeParseReturnType<Input, Output>> {
return this.safeParseAsync(data, params);
}
}

/////////////////////////////////////////
Expand Down
148 changes: 148 additions & 0 deletions src/__tests__/convert.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
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<string>();
expectTypeOf(stringToNumber.convert).returns.toMatchTypeOf<number>();

expectTypeOf(numberToString.convert).toBeFunction();
expectTypeOf(numberToString.convert).parameter(0).toMatchTypeOf<number>();
expectTypeOf(numberToString.convert).returns.toMatchTypeOf<string>();
});

test("valid schema conversion", () => {
const userSchema = z.object({
id: z.string(),
name: z.string(),
age: z.string().transform((age) => parseInt(age, 10)),
});

const validInput: z.input<typeof userSchema> = {
id: "123",
name: "Alice",
age: "25",
};

const user = userSchema.convert(validInput);
expectTypeOf(user).toMatchTypeOf<z.infer<typeof userSchema>>();
});

test("invalid schema conversion", () => {
const userSchema = z.object({
id: z.string(),
name: z.string(),
age: z.string().transform((age) => parseInt(age, 10)),
});

// Input not matching the schema's input type
const invalidInput = {
name: "Alice",
age: "25",
};

expectTypeOf(numberToString.convert)
.parameter(0)
.not.toMatchTypeOf<typeof invalidInput>();

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<string>();
expectTypeOf(stringToNumber.safeConvert).returns.toMatchTypeOf<
SafeParseReturnType<string, number>
>();

const resA = stringToNumber.safeConvert(""); // valid input but invlaid output
if (resA.success) {
assertType<number>(resA.data);
} else {
assertType<z.ZodError>(resA.error);
}

expectTypeOf(numberToString.safeConvert).toBeFunction();
expectTypeOf(numberToString.safeConvert)
.parameter(0)
.toMatchTypeOf<number>();
expectTypeOf(numberToString.safeConvert).returns.toMatchTypeOf<
SafeParseReturnType<number, string>
>();

const resB = numberToString.safeConvert(321);
if (resB.success) {
assertType<string>(resB.data);
} else {
assertType<z.ZodError>(resB.error);
}
});
});

describe("convertAsync", () => {
test("input and output types to be enforced in", () => {
expectTypeOf(asyncStringToNumber.convertAsync).toBeFunction();
expectTypeOf(asyncStringToNumber.convertAsync)
.parameter(0)
.toMatchTypeOf<string>();
expectTypeOf(asyncStringToNumber.convertAsync).returns.toMatchTypeOf<
Promise<number>
>();

expectTypeOf(asyncNumberToString.convertAsync).toBeFunction();
expectTypeOf(asyncNumberToString.convertAsync)
.parameter(0)
.toMatchTypeOf<number>();
expectTypeOf(asyncNumberToString.convertAsync).returns.toMatchTypeOf<
Promise<string>
>();
});
});

describe("safeConvertAsync", () => {
test("input and output types to be enforced in", async () => {
expectTypeOf(asyncStringToNumber.safeConvertAsync).toBeFunction();
expectTypeOf(asyncStringToNumber.safeConvertAsync)
.parameter(0)
.toMatchTypeOf<string>();
expectTypeOf(asyncStringToNumber.safeConvertAsync).returns.toMatchTypeOf<
Promise<SafeParseReturnType<string, number>>
>();

const resA = await asyncStringToNumber.safeConvertAsync(""); // valid input but invlaid output
if (resA.success) {
assertType<number>(resA.data);
} else {
assertType<z.ZodError>(resA.error);
}

expectTypeOf(asyncNumberToString.safeConvertAsync).toBeFunction();
expectTypeOf(asyncNumberToString.safeConvertAsync)
.parameter(0)
.toMatchTypeOf<number>();
expectTypeOf(asyncNumberToString.safeConvertAsync).returns.toMatchTypeOf<
Promise<SafeParseReturnType<number, string>>
>();

const resB = await asyncNumberToString.safeConvertAsync(321);
if (resB.success) {
assertType<string>(resB.data);
} else {
assertType<z.ZodError>(resB.error);
}
});
});
22 changes: 22 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,28 @@ export abstract class ZodType<
isNullable(): boolean {
return this.safeParse(null).success;
}

convert(data: Input, params?: Partial<ParseParams>): Output {
return this.parse(data, params);
}

safeConvert(
data: Input,
params?: Partial<ParseParams>
): SafeParseReturnType<Input, Output> {
return this.safeParse(data, params);
}

convertAsync(data: Input, params?: Partial<ParseParams>): Promise<Output> {
return this.parseAsync(data, params);
}

safeConvertAsync(
data: Input,
params?: Partial<ParseParams>
): Promise<SafeParseReturnType<Input, Output>> {
return this.safeParseAsync(data, params);
}
}

/////////////////////////////////////////
Expand Down

0 comments on commit dad8820

Please sign in to comment.