2022/11/06
2022/11/06
こんにちは、@yamakenjiです。 Twitterを眺めていると、なにやらZodを試せるチュートリアルなるものがあることを知ったので、 これは試すしかないとおもって試してみました。
Zodとは、TypeScript-firstなスキーマ定義とバリデーションを行うライブラリです。
Astroがスポンサー枠にいたり、blitzやtRPCなどに
使われています。
重複した型定義をなくすことを目的としており、以下の特徴を持っています。
チュートリアルと書いてますが、あらかじめ型エラーかランタイムエラーが発生するサンプル問題とテストコードが用意されており、テストを通しながらZodの基本的な使い方を学んでいきます。 https://github.com/total-typescript/zod-tutorialに公開されていて、手元で動かしながら問題を解くことができます。 全14問あり、以下のような構成になっています。
1~3までの問題では、主にZodの基本的な使い方を学ぶことができます。TypeScriptのプリミティブな値に対してスキーマ定義を行い、validationを行います。
4~9までの問題では、inferやunionといったTypeScriptの型の組み合わせをZodでどう扱うかみたいな問題を学ぶことができます。
10~からは、実際に独自のvalidationルールを書きながらスキーマ定義も行うといったZodらしい、Zodを実際に利用する上でのカバーポイントみたいな問題を学べます。
実際にコードベースではどんな使い方をするのかというところで、第3問目と第11問目を紹介させていただきます。
自分がチュートリアルを通して解答したものも載せておきます。自分の解答
この問題では、連想配列をスキーマ定義していきます。
求める型定義は以下のようなものです
type Answer = {
results: {
name: string;
}[]
}
const answer: Answer = {
results: [{name: 'one'}, {name: 'two'}]
}
これに対してZodを使用したスキーマ定義では以下のようになります。
import { z } from "zod";
const StarWarsPerson = z.object({
name: z.string(),
});
const StarWarsPeopleResults = z.object({
results: StarWarsPerson.array(),
});
z.string()
やz.object()
でスキーマ定義を行っています。また、z.array()
でも配列を定義することは可能ですが、一度定義したZodSchemaはチェインで.array()
みたいにつなぐこともできます。
これらを組み合わせて、上記では連想配列をスキーマ定義しています。
export const fetchStarWarsPeople = async () => {
---
const parsedData = StarWarsPeopleResults.parse(data);
};
定義したスキーマに対して、parseメソッドを用いてデータをparse、validationしていきます。スキーマを満たさないデータが渡されていたらエラーをはいてくれます。
この問題では、定義したスキーマをもとに、任意のvalidationロジックを実装していきます。
import { z } from "zod";
const Form = z
.object({
password: z.string(),
confirmPassword: z.string(),
})
.refine(
(val) => {
return val.password === val.confirmPassword;
},
{
message: "Passwords don't match",
path: ["confirmPassword"],
}
);
z.object()
までは、Object型のスキーマ定義を行っており、それに対してrefineを用いて等価処理を行っています。
refineの第一引数には実装したいvalidationロジックを、第二引数にエラーメッセージやエラーパスといったオプションを指定することができます。
公式Zod refine docs
Zodのチュートリアルと簡単な使い方をご紹介させていただきました
型定義を行いながらvalidationロジックも簡単に書けるのはすごく魅力的だなと思いました。また、emailやurlといったものをちゃんと検証するには正規表現などを書く必要がありますが、Zodでは内包してくれているので(z.string().url()とか)、簡単な検証ならこれで済ませることができるのもいいなと思いました。(厳密に独自に定義したかったら自分で書く必要がありそう・・?)
また、react-hook-formと組み合わせて使えたり、環境変数、Next.jsのAPI Routesとかにも使えそうなので、使えるところは使って安心な型生活を送りたいなと思います。