📥
zodを利用したquery stringのparse
検索結果画面等で検索条件をquery stringで渡す際、どうやってparseするのがbetterか毎回モヤモヤしていた。
最近、zodでいい感じにparseできているのでメモがてら共有してみる。
概要
schemaを作ってsafeParseして、trueならその値を、falseならdefault値を返す。
たとえばidとかの自然数の場合、
export const checkIsPositiveInt = (target: unknown): target is number =>
z.number().int().positive().safeParse(target).success;
export const parseToPositiveInt = <T>(target: unknown, defailtValue: T) => {
return checkIsPositiveInt(Number(target)) ? Number(target) : defailtValue;
};
を用意しておいて、以下のように使う
const parsedPage = parseToPositiveInt(query?.page, 1)
水平展開
例えば自然数配列とかbool値は今のところ以下みたいな感じ。
export const checkIsIntArray = (target: unknown): target is number[] =>
z.array(z.number().int()).safeParse(target).success;
export const parseToIntArray = <T>(target: unknown, defailtValue: T) => {
return target != null &&
Array.isArray(target) &&
checkIsIntArray(target.map((v) => Number(v)))
? target.map((v) => Number(v))
: defailtValue;
};
export const checkIsBoolean = (target: unknown): target is boolean =>
z.boolean().safeParse(target).success;
export const parseToBoolean = <T>(target: unknown, defailtValue: T) => {
const convertedTarget =
target === 'true' ? true : target === 'false' ? false : target;
return checkIsBoolean(convertedTarget) ? convertedTarget : defailtValue;
};
これらを使って検索条件などを以下みたいな感じでparseする。
export const parseQueryString = (query: ParsedUrlQuery): PageQuery => {
const {
page,
perPage,
categoryIds,
isRecommended,
} = unflatten<ParsedUrlQuery, { [key in keyof PageQuery]?: unknown }>(query);
return {
page: parseToPositiveInt(page, 1),
perPage: parseToPositiveInt(perPage, PER_PAGE_OPTIONS[0].value),
categoryIds: parseToIntArray(categoryIds, []),
isRecommended: parseToBoolean(isRecommended, false),
};
};
zod v3.20で追加されたcoerce
を使えばより簡潔に書けるのかな?
いい感じのparse方法があればコメントで教えていただけると幸いです。
Discussion
軽く調べただけでしたが、以下のワークアラウンドを見つけました。
coerce でできるぽそうですね。
簡単ですが、以上です。
Bool値のNice化はtransformでハンドリングしてみました。
src/features/search/domains/searchQueryParams.ts
src/features/search/hooks/useTypedRouter.ts
src/pages/search/index.tsx
/search
ページがデモになります。簡単ですが、以上です。