【Zod】ErrorMapでエラーメッセージをカスタマイズして共通化する
const userSchema = z.object({ id: z.string().regex(/^[a-z0-9]+$/, { message: '不正なIDです' }), name: z .string() .min(1, { message: '未入力です' }) .max(20, { message: '20文字以内で入力してください' }), bio: z .string() .min(20, { message: '20文字以上で入力してください' }) .max(255, { message: '255文字以内で入力してください' }), email: z.string().email({ message: '不正なメールアドレスです' }),});
- Zodでエラーメッセージを設定する際、上記のようにスキーマ毎に
message
を指定する方法が一般的かもしれない - しかしこの方法だとスキーマ定義のコード量が増えるだけでなく設定ミスなども発生しやすくなるので、いい感じにエラーメッセージを共通化したい
- デフォルトのメッセージは英語なので日本語化したい
ZodErrorMapを使う方法
const customErrorMap: z.ZodErrorMap = (issue, ctx) => { switch (issue.code) { case z.ZodIssueCode.too_big: if (issue.type === 'string') { return { message: `${issue.maximum}文字以内で入力してください` }; } break;
case z.ZodIssueCode.too_small: if (issue.type === 'string') { if (issue.minimum === 1) return { message: '未入力です' }; return { message: `${issue.minimum}文字以上で入力してください` }; } }
return { message: ctx.defaultError };};
z.setErrorMap(customErrorMap);
- ZodErrorMapでエラー内容に応じた任意のメッセージを設定
z.setErrorMap
することでグローバルにこの設定が反映される
z.string().min(1).safeParse('').error?.issues[0].message; // '未入力です'z.string().max(255).safeParse('a'.repeat(256)).error?.issues[0].message; // '255文字以内で入力してください'
今回は入力値がstring
のケースのみを考慮したErrorMapではあるが、これでスキーマ毎にmessage
を指定する必要もなく、エラーメッセージを共通化できた。
z.string({ errorMap: customErrorMap });
スキーマ毎にErrorMapを指定することも可能。
const specificErrorMap: z.ZodErrorMap = (issue, ctx) => { switch (issue.code) { // some message settings }
return { message: customErrorMap(issue, ctx).message };};
z.string({ errorMap: specificErrorMap });
そのためグローバルに設定したErrorMapを継承しつつ、より具体的なErrorMapで上書きする、ということも可能。
superRefineを使う方法
const minRefinement = (min: number): z.SuperRefinement<string> => (val, ctx) => { if (val.length < min) { ctx.addIssue({ code: z.ZodIssueCode.too_small, minimum: min, type: 'string', inclusive: true, message: `${min}文字以上で入力してください`, }); } };
const maxRefinement = (max: number): z.SuperRefinement<string> => (val, ctx) => { if (val.length > max) { ctx.addIssue({ code: z.ZodIssueCode.too_big, maximum: max, type: 'string', inclusive: true, message: `${max}文字以内で入力してください`, }); } };
z.string().superRefine(minRefinement(1)).superRefine(maxRefinement(255));
superRefine
に渡す関数を動的に生成し、エラーメッセージ (とロジック) を共通化する方法- 苦し紛れなので正直前述のErrorMapを使う方が無難
- Zodから提供される検証ロジックを利用できない
superRefine
を多用することになるのでスキーマの可読性が下がる