フロントエンドのフォームのバリデーションとしてzod
を利用しました。
エラーメッセージのカスタマイズ方法がなかなか見つからなかったので、記しておきます。
なお、使用しているzod
のバージョンは以下です。
zod@3.19.1
zodとは
定義したバリデーションスキーマからTypeScript
の型を生成してくれます。
これにより、型定義とバリデーション定義を一括管理できます
// バリデーションスキーマ
const userSchema = zod.object({
// テキストのバリデーション
userId: zod
.string()
.regex(/^[a-zA-Z0-9]+$/)
.min(5)
.max(15),
// チェックボックス(複数選択可)のバリデーション
interest: zod.array(zod.string()).min(1),
// チェックボックス(項目が1つだけ。値がtrue or false)のバリデーション
// trueのみ許可
confirm: zod.literal(true),
});
// バリデーションスキーマから型定義を作成
type userValues = zod.infer<typeof userSchema>;
// type UserValues = {
// userId: string;
// interest: string[];
// confirm: true;
// }
zodのエラーメッセージをカスタマイズする
zod
のバリデーションエラーメッセージはデフォルトで英語になっています。
そのため、日本語で出力できるようにエラーメッセージをカスタマイズします。
方法1: 引数として渡す
一番簡単な方法です。
スキーマまたは各バリデーションメソッドの引数として渡します。
(このやり方は知っている!でもこのやり方は嫌なんだ!って方は方法2
を参考にしてください。)
import * as zod from 'zod';
// オブジェクトスキーマ
const userSchema = zod.object({
userId: zod
// stringスキーマのエラーメッセージ
.string({ required_error: '必須項目です', invalid_type_error: '入力値に誤りがります' })
// 以下バリデーションメソッドの追加引数としてエラーメッセージを指定
.regex(/^[a-zA-Z0-9]+$/, { message: '半角英数字で入力してください' })
.min(5, { message: '5文字以上で入力してください。' })
.max(15, { message: '15文字以下で入力してください。' }),
interest: zod
.array(zod.string({ required_error: '必須項目です', invalid_type_error: '入力値に誤りがります' }))
.min(1, { message: '1つ以上選択してください' }),
// literalは引数でmessageを渡せない
confirm: zod.literal(true),
});
中には引数でメッセージを渡せないものもあります。
そのため、全てのバリデーションメソッドでこの方法が利用できるわけではありません!
また、個別にメッセージを指定するのは不便です
方法2: ZodErrorMapでエラーメッセージをカスタマイズする
以下の様にZodErrorMap
を用いて、zod
のエラーコードごとにメッセージをカスタマイズします。
import * as zod from 'zod';
/**
* zodエラーメッセージ(日本語)
*/
const customErrorMap: zod.ZodErrorMap = (issue, ctx) => {
// zodエラーコードごとにメッセージをカスタマイズする
switch (issue.code) {
// 型に誤り
case zod.ZodIssueCode.invalid_type:
// undefinedだった場合は未入力判定
if (issue.received === zod.ZodParsedType.undefined) {
return { message: '必須項目です' };
} else {
return { message: '値に誤りがあります' };
}
case zod.ZodIssueCode.too_big:
return { message: `${issue.maximum}文字以内で入力してください` };
case zod.ZodIssueCode.too_small:
if (issue.type === 'array') {
return { message: `${issue.minimum}つ以上チェックしてください` };
}
return { message: `${issue.minimum}文字以上で入力してください` };
}
// デフォルトのメッセージを返す
return { message: ctx.defaultError };
};
上記条件を満たさないエラーの場合は、デフォルトのエラーメッセージを出力します。
独自のZodErrorMap
を作成する時は、zod
のデフォルトメッセージをセットしているファイルが参考になります!
ZodErrorMap について
https://github.com/colinhacks/zod/blob/master/ERROR_HANDLING.md#customizing-errors-with-zoderrormap
-
{ message: string }
を返す必要がある -
issue
はどういったエラーであったかというデータからmessage
を除外したエラー詳細情報 -
ctx.default
はデフォルトのエラーマップによって生成されたエラーメッセージ -
src/ZodError.ts
で定義されたdefaultErrorMap
の優先順位が最も低く、上書きされたメッセージ(カスタマイズされたエラーメッセージ)の方が優先順位が高い
カスタマイズしたZodErrorMap
をグローバルに適用する
カスタムのZodErrorMap
をグローバルに適用するには.setErrorMap()
に指定するだけです。
// zodカスタムエラーメッセージをグローバルに適用
zod.setErrorMap(customErrorMap);
zod
をimport
しているファイルであるならば.setErrorMap()
を呼び出せます。
ただ、適用範囲がグローバルなので、どこで呼び出すのかはプロジェクトごとにルールを決めたほうが良いでしょう。
カスタマイズしたZodErrorMap
を個別のスキーマにバインドする
グローバルに適用させるのはちょっと...という場合には、
カスタマイズしたZodErrorMap
を個別のスキーマにバインドする(デフォルトエラーをカスタムエラーで上書きする)こともできます。
const userSchema = zod.object({
userId: zod
+ .string({ errorMap: customErrorMap })
.regex(/^[a-zA-Z0-9]+$/)
.min(5)
.max(15),
+ interest: zod.array(zod.string({ errorMap: customErrorMap })).min(1),
confirm: zod.literal(true),
});
上記ではオプションのerrorMap
の値として、今回作成したcustomErrorMap
を設定しています。
これで以下の様な出力になります。
-
userId
,interest
:customErrorMap
のエラーメッセージが出力される -
confirm
:デフォルトのエラーメッセージが出力される
共通のメッセージはグローバルのZodErrorMap
から出力し、一部の項目は個別のスキーマにバインドしたZodErrorMap
から出力する
これがよくあるパターンかなと思います。
.max
などは共通で設定されているエラーメッセージから出力し、一部項目は個別のスキーマ単位で設定する方法です。
先程のcustomErrorMap
はグローバルに適用するように設定します。
// zodカスタムエラーメッセージをグローバルに適用
zod.setErrorMap(customErrorMap);
このuserSchema
のエラー時だけ利用する、カスタムのuserSchemaErrorMap
を作成します。
import * as zod from 'zod';
import { customErrorMap } from '@/util/zod/customErrorMap';
/**
* userSchema用カスタムエラーメッセージ
*/
const userSchemaErrorMap: zod.ZodErrorMap = (issue, ctx) => {
switch (issue.code) {
case zod.ZodIssueCode.invalid_literal:
// true/falseチェックボックス用メッセージ
return { message: 'チェック必須です' };
case zod.ZodIssueCode.invalid_string:
if (issue.validation === 'regex') {
return { message: '半角英数字で入力してください。' };
}
}
// グローバルErrorMapのメッセージを返す
return { message: customErrorMap(issue, ctx).message };
};
グローバルのエラーマップとスキーマのエラーマップの違い
グローバルのエラーマップ(customErrorMap
)と、
スキーマのエラーマップ(userSchemaErrorMap
)の違いは、
いずれのエラーコードにも合致しないエラーが発生した場合のメッセージの指定です。
グローバルのエラーマップでは、zod
のデフォルトメッセージを返しています。
// デフォルトのメッセージを返す
return { message: ctx.defaultError };
一方スキーマのエラーマップでは、グローバルのcustomErrorMap
のメッセージを返しています。
// グローバルErrorMapのメッセージを返す
return { message: customErrorMap(issue, ctx).message };
こう指定することで、
スキーマのエラーマップ
から該当するエラーメッセージを探す
→ グローバルのエラーマップ
から該当するエラーメッセージを探す
→ デフォルトのエラーメッセージを探す
となります。
これによって
共通のメッセージは、グローバルのZodErrorMap
から出力し、
一部の項目は、個別のスキーマにバインドしたZodErrorMap
から出力する
が実現できます。
最後に、このスキーマのエラーマップ(userSchemaErrorMap
)を適用したい項目に設定します。
const userSchema = zod.object({
userId: zod
+ .string({ errorMap: userSchemaErrorMap })
.regex(/^[a-zA-Z0-9]+$/)
.min(5)
.max(15),
interest: zod.array(zod.string()).min(1),
+ confirm: zod.literal(true, { errorMap: userSchemaErrorMap }),
});
上記の場合、以下の様な出力になります。
-
.regex
,.literal
のエラー:userSchemaErrorMap
からエラーメッセージを表示する -
.min
,.max
のエラー:グローバルのcustomErrorMap
からエラーメッセージを表示する
ZodErrorMap
を活用して、良きzod
ライフを!