158 lines
4.6 KiB
TypeScript
158 lines
4.6 KiB
TypeScript
// @ts-ignore TS6133
|
|
import { expect, test } from "vitest";
|
|
|
|
import * as z from "zod/v3";
|
|
import { util } from "../helpers/util.js";
|
|
|
|
const Test = z.object({
|
|
f1: z.number(),
|
|
f2: z.string().optional(),
|
|
f3: z.string().nullable(),
|
|
f4: z.array(z.object({ t: z.union([z.string(), z.boolean()]) })),
|
|
});
|
|
type TestFlattenedErrors = z.inferFlattenedErrors<typeof Test, { message: string; code: number }>;
|
|
type TestFormErrors = z.inferFlattenedErrors<typeof Test>;
|
|
|
|
test("default flattened errors type inference", () => {
|
|
type TestTypeErrors = {
|
|
formErrors: string[];
|
|
fieldErrors: { [P in keyof z.TypeOf<typeof Test>]?: string[] | undefined };
|
|
};
|
|
|
|
util.assertEqual<z.inferFlattenedErrors<typeof Test>, TestTypeErrors>(true);
|
|
util.assertEqual<z.inferFlattenedErrors<typeof Test, { message: string }>, TestTypeErrors>(false);
|
|
});
|
|
|
|
test("custom flattened errors type inference", () => {
|
|
type ErrorType = { message: string; code: number };
|
|
type TestTypeErrors = {
|
|
formErrors: ErrorType[];
|
|
fieldErrors: {
|
|
[P in keyof z.TypeOf<typeof Test>]?: ErrorType[] | undefined;
|
|
};
|
|
};
|
|
|
|
util.assertEqual<z.inferFlattenedErrors<typeof Test>, TestTypeErrors>(false);
|
|
util.assertEqual<z.inferFlattenedErrors<typeof Test, { message: string; code: number }>, TestTypeErrors>(true);
|
|
util.assertEqual<z.inferFlattenedErrors<typeof Test, { message: string }>, TestTypeErrors>(false);
|
|
});
|
|
|
|
test("form errors type inference", () => {
|
|
type TestTypeErrors = {
|
|
formErrors: string[];
|
|
fieldErrors: { [P in keyof z.TypeOf<typeof Test>]?: string[] | undefined };
|
|
};
|
|
|
|
util.assertEqual<z.inferFlattenedErrors<typeof Test>, TestTypeErrors>(true);
|
|
});
|
|
|
|
test(".flatten() type assertion", () => {
|
|
const parsed = Test.safeParse({}) as z.SafeParseError<void>;
|
|
const validFlattenedErrors: TestFlattenedErrors = parsed.error.flatten(() => ({ message: "", code: 0 }));
|
|
// @ts-expect-error should fail assertion between `TestFlattenedErrors` and unmapped `flatten()`.
|
|
const invalidFlattenedErrors: TestFlattenedErrors = parsed.error.flatten();
|
|
const validFormErrors: TestFormErrors = parsed.error.flatten();
|
|
// @ts-expect-error should fail assertion between `TestFormErrors` and mapped `flatten()`.
|
|
const invalidFormErrors: TestFormErrors = parsed.error.flatten(() => ({
|
|
message: "string",
|
|
code: 0,
|
|
}));
|
|
|
|
[validFlattenedErrors, invalidFlattenedErrors, validFormErrors, invalidFormErrors];
|
|
});
|
|
|
|
test(".formErrors type assertion", () => {
|
|
const parsed = Test.safeParse({}) as z.SafeParseError<void>;
|
|
const validFormErrors: TestFormErrors = parsed.error.formErrors;
|
|
// @ts-expect-error should fail assertion between `TestFlattenedErrors` and `.formErrors`.
|
|
const invalidFlattenedErrors: TestFlattenedErrors = parsed.error.formErrors;
|
|
|
|
[validFormErrors, invalidFlattenedErrors];
|
|
});
|
|
|
|
test("all errors", () => {
|
|
const propertySchema = z.string();
|
|
const schema = z
|
|
.object({
|
|
a: propertySchema,
|
|
b: propertySchema,
|
|
})
|
|
.refine(
|
|
(val) => {
|
|
return val.a === val.b;
|
|
},
|
|
{ message: "Must be equal" }
|
|
);
|
|
|
|
try {
|
|
schema.parse({
|
|
a: "asdf",
|
|
b: "qwer",
|
|
});
|
|
} catch (error) {
|
|
if (error instanceof z.ZodError) {
|
|
expect(error.flatten()).toEqual({
|
|
formErrors: ["Must be equal"],
|
|
fieldErrors: {},
|
|
});
|
|
}
|
|
}
|
|
|
|
try {
|
|
schema.parse({
|
|
a: null,
|
|
b: null,
|
|
});
|
|
} catch (_error) {
|
|
const error = _error as z.ZodError;
|
|
expect(error.flatten()).toEqual({
|
|
formErrors: [],
|
|
fieldErrors: {
|
|
a: ["Expected string, received null"],
|
|
b: ["Expected string, received null"],
|
|
},
|
|
});
|
|
|
|
expect(error.flatten((iss) => iss.message.toUpperCase())).toEqual({
|
|
formErrors: [],
|
|
fieldErrors: {
|
|
a: ["EXPECTED STRING, RECEIVED NULL"],
|
|
b: ["EXPECTED STRING, RECEIVED NULL"],
|
|
},
|
|
});
|
|
// Test identity
|
|
|
|
expect(error.flatten((i: z.ZodIssue) => i)).toEqual({
|
|
formErrors: [],
|
|
fieldErrors: {
|
|
a: [
|
|
{
|
|
code: "invalid_type",
|
|
expected: "string",
|
|
message: "Expected string, received null",
|
|
path: ["a"],
|
|
received: "null",
|
|
},
|
|
],
|
|
b: [
|
|
{
|
|
code: "invalid_type",
|
|
expected: "string",
|
|
message: "Expected string, received null",
|
|
path: ["b"],
|
|
received: "null",
|
|
},
|
|
],
|
|
},
|
|
});
|
|
// Test mapping
|
|
expect(error.flatten((i: z.ZodIssue) => i.message.length)).toEqual({
|
|
formErrors: [],
|
|
fieldErrors: {
|
|
a: ["Expected string, received null".length],
|
|
b: ["Expected string, received null".length],
|
|
},
|
|
});
|
|
}
|
|
});
|