Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add custom error message on nativeEnum validation #3158

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions deno/lib/__tests__/enum.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,19 +59,27 @@ test("extract/exclude", () => {
util.assertEqual<z.infer<typeof EmptyFoodEnum>, never>(true);
});

test('error map in extract/exclude', () => {
test("error map in extract/exclude", () => {
const foods = ["Pasta", "Pizza", "Tacos", "Burgers", "Salad"] as const;
const FoodEnum = z.enum(foods, { errorMap: () => ({ message: "This is not food!" }) });
const FoodEnum = z.enum(foods, {
errorMap: () => ({ message: "This is not food!" }),
});
const ItalianEnum = FoodEnum.extract(["Pasta", "Pizza"]);
const foodsError = FoodEnum.safeParse("Cucumbers");
const italianError = ItalianEnum.safeParse("Tacos");
if (!foodsError.success && !italianError.success) {
expect(foodsError.error.issues[0].message).toEqual(italianError.error.issues[0].message);
expect(foodsError.error.issues[0].message).toEqual(
italianError.error.issues[0].message
);
}

const UnhealthyEnum = FoodEnum.exclude(["Salad"], { errorMap: () => ({ message: "This is not healthy food!" }) });
const UnhealthyEnum = FoodEnum.exclude(["Salad"], {
errorMap: () => ({ message: "This is not healthy food!" }),
});
const unhealthyError = UnhealthyEnum.safeParse("Salad");
if (!unhealthyError.success) {
expect(unhealthyError.error.issues[0].message).toEqual("This is not healthy food!");
expect(unhealthyError.error.issues[0].message).toEqual(
"This is not healthy food!"
);
}
});
37 changes: 37 additions & 0 deletions deno/lib/__tests__/error.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,43 @@ test("when the message is falsy, it is used as is provided", () => {
}
});

test("nativeEnum with invalid_type_error returns custom error message", () => {
enum OrderStatusEnum {
NEW = "NEW",
PROCESSED = "PROCESSED",
}

const schema = z.nativeEnum(OrderStatusEnum, {
invalid_type_error: "the value provided is invalid",
required_error: "status is required",
});

const result1 = schema.safeParse("UNPROCESSEED");
expect(result1.success).toEqual(false);
if (!result1.success) {
expect(result1.error.issues[0].message).toEqual(
"the value provided is invalid"
);
}

const result2 = schema.safeParse(undefined);
expect(result2.success).toEqual(false);
if (!result2.success) {
expect(result2.error.issues[0].message).toEqual("status is required");
}

const result3 = schema.safeParse(OrderStatusEnum.NEW);
expect(result3.success).toEqual(true);

const result4 = schema.safeParse(null);
expect(result4.success).toEqual(false);
if (!result4.success) {
expect(result4.error.issues[0].message).toEqual(
"the value provided is invalid"
);
}
});

// test("dont short circuit on continuable errors", () => {
// const user = z
// .object({
Expand Down
3 changes: 3 additions & 0 deletions deno/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ function processCreateParams(params: RawCreateParams): ProcessedCreateParams {
}
if (errorMap) return { errorMap: errorMap, description };
const customMap: ZodErrorMap = (iss, ctx) => {
if (iss.code === "invalid_enum_value") {
return { message: invalid_type_error ?? ctx.defaultError };
}
if (iss.code !== "invalid_type") return { message: ctx.defaultError };
if (typeof ctx.data === "undefined") {
return { message: required_error ?? ctx.defaultError };
Expand Down
18 changes: 13 additions & 5 deletions src/__tests__/enum.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,19 +58,27 @@ test("extract/exclude", () => {
util.assertEqual<z.infer<typeof EmptyFoodEnum>, never>(true);
});

test('error map in extract/exclude', () => {
test("error map in extract/exclude", () => {
const foods = ["Pasta", "Pizza", "Tacos", "Burgers", "Salad"] as const;
const FoodEnum = z.enum(foods, { errorMap: () => ({ message: "This is not food!" }) });
const FoodEnum = z.enum(foods, {
errorMap: () => ({ message: "This is not food!" }),
});
const ItalianEnum = FoodEnum.extract(["Pasta", "Pizza"]);
const foodsError = FoodEnum.safeParse("Cucumbers");
const italianError = ItalianEnum.safeParse("Tacos");
if (!foodsError.success && !italianError.success) {
expect(foodsError.error.issues[0].message).toEqual(italianError.error.issues[0].message);
expect(foodsError.error.issues[0].message).toEqual(
italianError.error.issues[0].message
);
}

const UnhealthyEnum = FoodEnum.exclude(["Salad"], { errorMap: () => ({ message: "This is not healthy food!" }) });
const UnhealthyEnum = FoodEnum.exclude(["Salad"], {
errorMap: () => ({ message: "This is not healthy food!" }),
});
const unhealthyError = UnhealthyEnum.safeParse("Salad");
if (!unhealthyError.success) {
expect(unhealthyError.error.issues[0].message).toEqual("This is not healthy food!");
expect(unhealthyError.error.issues[0].message).toEqual(
"This is not healthy food!"
);
}
});
37 changes: 37 additions & 0 deletions src/__tests__/error.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,43 @@ test("when the message is falsy, it is used as is provided", () => {
}
});

test("nativeEnum with invalid_type_error returns custom error message", () => {
enum OrderStatusEnum {
NEW = "NEW",
PROCESSED = "PROCESSED",
}

const schema = z.nativeEnum(OrderStatusEnum, {
invalid_type_error: "the value provided is invalid",
required_error: "status is required",
});

const result1 = schema.safeParse("UNPROCESSEED");
expect(result1.success).toEqual(false);
if (!result1.success) {
expect(result1.error.issues[0].message).toEqual(
"the value provided is invalid"
);
}

const result2 = schema.safeParse(undefined);
expect(result2.success).toEqual(false);
if (!result2.success) {
expect(result2.error.issues[0].message).toEqual("status is required");
}

const result3 = schema.safeParse(OrderStatusEnum.NEW);
expect(result3.success).toEqual(true);

const result4 = schema.safeParse(null);
expect(result4.success).toEqual(false);
if (!result4.success) {
expect(result4.error.issues[0].message).toEqual(
"the value provided is invalid"
);
}
});

// test("dont short circuit on continuable errors", () => {
// const user = z
// .object({
Expand Down
3 changes: 3 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ function processCreateParams(params: RawCreateParams): ProcessedCreateParams {
}
if (errorMap) return { errorMap: errorMap, description };
const customMap: ZodErrorMap = (iss, ctx) => {
if (iss.code === "invalid_enum_value") {
return { message: invalid_type_error ?? ctx.defaultError };
}
if (iss.code !== "invalid_type") return { message: ctx.defaultError };
if (typeof ctx.data === "undefined") {
return { message: required_error ?? ctx.defaultError };
Expand Down
Loading