はじめに
Zod というバリデーションライブラリが非常に流行っているようなので、素振りした。
このチュートリアルはたしか Twitter で流れてきて知ったのだが
今見ると Zod の公式ドキュメントからも Resources として紹介されているので、そこそこ信頼していいコンテンツなのだと判断した。
チュートリアルについて
チュートリアルと名がついているが、内容は全 10 問のエクササイズを解くという構成。
あらかじめ型チェックのエラーまたはランタイムのエラーが発生するサンプルコードが問題として用意されており、そのコードを修正しながら Zod の基本的な使い方を学ぶ。 チュートリアルには Zod の使い方の説明は特にないので、チュートリアルの問題を解くために Zod の公式ドキュメントを読んで必要な箇所を理解する、という感じ。
Rust でいう rustlings に似てるなと思った。
Web エディタでも解けるし、 https://github.com/total-typescript/zod-tutorial にもコードが公開されており、git clone して手元で動かしながら問題を解くこともできる。
また、全 10 問といったがリポジトリには隠しステージっぽく 11〜14 問目まである。
各問で学べるのは(自分の理解では)以下。
- Runtime Type Checking with Zod
- 基本。プリミティブな値 (number) に対するスキーマ定義および
parse
メソッドæ
- 基本。プリミティブな値 (number) に対するスキーマ定義および
- Verify Unknown APIs with an Object Schema
- Create an Array of Custom Types
z.array
による配列の定義方法
- Extract a Type from a Passer Object
z.infer<typeof スキーマ>
でスキーマから型の取り出し
- Make Schemas Optional
- object の特定の key を optional にする方法
- スキーマに
.optional()
つけると optional にできる - https://zod.dev/?id=optional
- Set a Default Value with Zod
- 初期値をセットする方法
- https://zod.dev/?id=default
- Be Specific with Allowed Types
- Union の使い方
- https://zod.dev/?id=unions
- ℹ️ なにげに
z.literal
もここで初登場
- Complex Schema Validation
- https://zod.dev/?id=strings
- string は文字列長の min/max だけでなく、
email
やurl
の形式を強制する便利なメソッドがある
- Reduce Duplicated Code by Composing Schemas
- Transform Data from Within a Schema
.transform
を使うとパース後のデータの変換ができる
終わった後に公式ドキュメントをざっと眺めて、たしかに主要なところはカバーしてるのかなと思った。
学び:Zod の基本
ここからはチュートリアルプラス公式ドキュメントも読んで学んだことをメモ。
Zod の特徴
Introduction に挙げられてるのは以下。
- Zero dependencies
- Node.js やすべてのモダンブラウザで動く
- 小さい:minified + zipped で 8kb
- イミュータブル
- 簡潔でチェイン可能なインターフェース
- 関数型アプローチ: parse, don’t validate (←これリンク先の記事まで見てないのでわからなかった)
- プレーンなJSでも動く
基本的な使い方
const stringSchema = z.string()
のようにプリミティブなスキーマも定義できるが、実際使うとなると多くがオブジェクト形式だと思うのでオブジェクトのスキーマ例を載せる。
import { z } from "zod"; const User = z.object({ username: z.string(), }); User.parse({ username: "Ludwig" }); // extract the inferred type type User = z.infer<typeof User>; // { username: string }
(コードは Basic usage より引用)
- 基本的には
z.object({ ... })
でスキーマ定義、z.infer<typeof スキーマ>
でスキーマから推論される型を抽出できる - 定義したスキーマの
parse
メソッドでデータをパースする。スキーマを満たさなければ ZodError が throw される - パースした結果、スキーマに定義された key のみを持つオブジェクトになる。なので 元データにあってスキーマに定義されてない key はパースによって取り除かれる
- チュートリアルの 2 Verify Unknown APIs with an Object Schema で強調されてたのでなんか記憶に残った
その他、 .shape
でオブジェクトの特定のkeyのスキーマを取得できたり、
User.shape.name; // => string schema
.merge
.pick
.omit
partial
など TypeScript の型操作的な API は一通り提供されている。
.refine
:カスタムバリデーション
独自のバリデーションロジックを実装したい場合は .refine
メソッドを使う。
const myString = z.string().refine((val) => val.length <= 255, { message: "String can't be more than 255 characters", });
(コードは公式ドキュメントより引用)
.transform
:パースした値の変換
定義したスキーマに続けて .transform
というメソッドを呼び出すと、パースした値を変換できる。
const StarWarsPerson = z .object({ name: z.string(), }) .transform((person) => ({ name: person.name, nameAsArray: person.name.split(" "), })); const person = { name: "Luke Skywalker", }; const parsed = StarWarsPerson.parse(person); console.log(parsed); // { name: 'Luke Skywalker', nameAsArray: [ 'Luke', 'Skywalker' ] }
Yup との違いは?
現職ではフォームのバリデーションに Yup を使っているので、違いが気になる。
最初、スキーマ定義から型を推論できるのは Zod すごいなって思ったけどそれは Yup でもできるらしい(InferType
)。知らなかった。。。
というわけでほとんど違いがわからずにいたが、公式ドキュメントの Comparison > Yup で比較されていた。何点か違いが列挙されているが、
- Yup はオブジェクトのすべてのフィールドがデフォルトで optional(Zod は required が基本)
- partial や deepPartial などのメソッドがない
- promise スキーマがない
- function スキーマがない
- union や intersection スキーマがない
というわけで、スキーマの充実度では Zod に分があるよという主張のよう。
また、 Yup の方が型推論がいまいちだ、という比較記事も見かけた。
参考:Reactで使えるバリデーションライブラリを紹介! - bagelee(ベーグリー)