ZodでIntFromString
やりたいこと
数値の文字列を、数値に変換する
e.g. "100"→100
数字でない場合は、拒否する
e.g. "hoge"は拒否する
この場合に、「数字を入力してください」というメッセージを表示したい
こうする
code:ts
const IntFromString = z
.string()
.regex(/^+-?\d*\.?\d+$/, { message: '数値を入力してください' }) .transform(Number);
以下のように使用する
entityでこういうのを用意しておいて
code:entity.ts
export const validatePrice = z.number().max(10000);
formやroutingでは以下のように使う
code:ts
const schema = z.object({
price: IntFromString.pipe(validatePrice)
})
応用
例えば、全角数字も許容したい、などの要件が追加されたとき
code:ts
const IntFromString = z
.string()
.regex(/^+-?\d*\.?\d+$/, { message: '数値を入力してください' }) .transform(Number);
export const IntFromZenHanString = z
.string()
.transform(zenkaku2hankaku)
.pipe(IntFromString);
ボツ案
coerce()を使う
code:ts
export const IntFromString = z.coerce.number()
数値でない文字列がNaNに変換されてしまい、扱いづらい
""が0に変換されるのも困る
preprocess()を使う
code:ts
export const IntFromString = z.preprocess(Number, z.number());
上記のcoerce()の劣化版という感じ
上記のcoerce()と同じ問題がある
また、inputがunknownになるのでIntFromStringという命名に沿ってない
string()で受け取った後に、refine().transform()する
code:ts
import { z } from 'zod';
export const IntFromString = z
.string()
.refine(v => Number.isInteger(+v) && +v > 0, '正数を入力してください')
.transform(v => +v);